Soru Çağrı ile başvuru arasındaki fark nedir?


Kullanma arasındaki fark nedir call ve apply bir işlevi çağırmak için?

var func = function() {
  alert('hello!');
};

func.apply(); vs func.call();

Yukarıda bahsedilen iki yöntem arasında performans farklılıkları var mı? En iyi ne zaman kullanılır? call üzerinde apply ve tersi?


2741
2017-12-31 19:56


Menşei


Düşün a args dizisi için geçerli ve cargs sütunları için çağrı. - Larry Battle
@LarryBattle Tamamen 70-480 sertifikamdaki farkı hatırlamama yardım etti :) - Shouvik
@ LarryBattle Neredeyse aynı şeyi yapıyorum, ama bence virgülle çağrılan dizi ve c için geçerli (yani virgülle ayrılmış argümanlar). - Samih
@LarryBattle güzel anımsatıcı Larry, nihayet isimlerle korelasyon hatırladı - Tomo
Sen uygulamak Bir kez bir iş için (bir argüman), sen [telefon] aramak insanlar birçok kez (çeşitli argümanlar). Alternatif: [çok?] Çok var Aramak Görev oyunları. - Gras Double


Cevaplar:


Fark şu ki apply işlevi ile çağırmanıza izin verir arguments bir dizi olarak; call parametrelerin açıkça listelenmesini gerektirir. Yararlı bir anımsatıcıdır "bir için birrray ve C için cOMMA."

MDN'nin belgelerine bakın uygulamak ve aramak.

Sözde sözdizimi:

theFunction.apply(valueForThis, arrayOfArgs)

theFunction.call(valueForThis, arg1, arg2, ...)

Ayrıca, ES6’dan itibaren spread ile kullanmak için dizi call işlev, uyumları görebilirsiniz İşte.

Basit kod:

function theFunction(name, profession) {
    console.log("My name is " + name + " and I am a " + profession +".");
}
theFunction("John", "fireman");
theFunction.apply(undefined, ["Susan", "school teacher"]);
theFunction.call(undefined, "Claude", "mathematician");
theFunction.call(undefined, ...["Matthew", "physicist"]); // used with the spread operator


3308
2017-12-31 20:00



Eklenecek bir şey, arjların sayısal bir dizi olması gerektiğidir ([]). İlişkilendirilmiş diziler ({}) çalışmayacak. - Kevin Schroeder
@KevinSchroeder: Javascript olarak, [] denir dizi, {} denir nesne. - Martijn
Sıklıkla bir diziyi kimin aldığını unuturdum ve bu da argümanları listelemenizi bekler. Hatırlamak için kullandığım bir teknik, yöntemin ilk harfinin bir o zaman bir dizi alır bir pply dizisi - aziz punjani
@SAM Kullanma aramak normal bir işlev çağrısı yerine sadece değerini değiştirmek gerekirse, sadece mantıklı bu işlev çağrısı için. Bir örnek (bir işlev arguments-objectini bir diziye dönüştüren): Array.prototype.slice.call(arguments) veya [].slice.call(arguments). uygulamak Bir dizideki argümanlara sahipseniz, örneğin (neredeyse) aynı parametrelerle başka bir işlevi çağıran bir işlevde anlam ifade eder. Tavsiye Normal işlev çağrısı kullan funcname(arg1) Eğer ihtiyacın olanı yaparsa ve saklarsa aramak ve uygulamak Onlara gerçekten ihtiyacınız olduğunda bu özel durumlar için. - some
@KunalSingh İkisi de call ve apply iki parametre alır. İlk argüman apply' and call` işlevi, sahip nesnesi olmalı ve ikinci parametre sırasıyla dizi veya virgülle ayrılmış parametreler olacaktır. Eğer geçersen null veya undefined İlk argüman olarak, katı olmayan modda, bunlar global nesne ile değiştirilir. window - A J Qarshi


K. Scott Allen güzel bir yazma konuyla ilgili.

Temel olarak, işlev argümanlarını nasıl ele aldıklarına göre farklılık gösterirler.

Apply () yöntemi, call () ile aynıdır, ancak apply () işlevi ikinci parametre olarak bir dizi gerektirir. Dizi, hedef yöntem için argümanları temsil eder. "

Yani:

// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);

209
2017-12-31 19:59



apply () ve call () 'ın ikinci parametresi isteğe bağlı değil, isteğe bağlıdır. - angry kiwi
İlk parametre de gerekli değil. - Ikrom


Her bir işlevi ne zaman kullanacağınız hakkında cevap vermek için apply Eğer geçecek argüman sayısını bilmiyorsanız ya da zaten bir dizi veya dizi benzeri bir nesnede bulunuyorsanız arguments Kendi argümanlarınızı iletmek için nesne. kullanım call aksi halde, argümanları bir diziye sarmaya gerek olmadığı için.

f.call(thisObject, a, b, c); // Fixed number of arguments

f.apply(thisObject, arguments); // Forward this function's arguments

var args = [];
while (...) {
    args.push(some_value());
}
f.apply(thisObject, args); // Unknown number of arguments

Herhangi bir argümanı geçmediğimde (örneğiniz gibi) tercih ederim call çünkü ben çağrı işlev. apply seni ima eder uygulayarak (varolmayan) argümanlara işlev.

Kullandığınız sürece, performans farklılıkları olmamalıdır. apply ve argümanları bir diziye sarın (ör. f.apply(thisObject, [a, b, c]) yerine f.call(thisObject, a, b, c)). Ben test etmedim, bu yüzden farklılıklar olabilir, ama tarayıcıya özel olurdu. Muhtemelen bu call bir dizide argümanlara sahip değilseniz daha hızlıdır ve apply Eğer yaparsan daha hızlıdır.


150
2017-12-31 21:50





İşte iyi bir anımsatıcı. birpply kullanır birrrays ve birlways bir veya iki Argüman alır. Kullandığında Ctek ihtiyacınız olan Cargüman sayısını teyit edin.


102
2017-09-04 13:36



Orada yararlı mnemonic var! 'Bir ya da iki Argüman' değerini 'ilk iki ya da ikinci parametreden beri' en fazla iki Argüman 'olarak değiştireceğim. apply gerekli. Eminim neden birisi arayacak? apply veya call parametre olmadan. Birisi neden burada olduğunu öğrenmeye çalışıyor gibi görünüyor stackoverflow.com/questions/15903782/... - dantheta
Yani, ne zaman Call () ve ne zaman Apply () kullanmak ... - Krish


Bu eski bir konu olsa da, sadece şunu belirtmek istedim. Bazıları .apply'dan biraz daha hızlı. Sana tam olarak nedenini anlatamam.

JsPerf'e bakın, http://jsperf.com/test-call-vs-apply/3


[UPDATE!]

Douglas Crockford, ikisinin arasındaki farktan kısaca bahsetti, bu da performans farkını açıklamaya yardımcı olabilir ... http://youtu.be/ya4UHuXNygM?t=15m52s

Uygula, Sıfır veya daha fazla bireysel parametre alırken, bir dizi argüman alır! Ah hah!

.apply(this, [...])

.call(this, param1, param2, param3, param4...)


91
2017-11-07 17:36



Bu, dizinin işlenmesi gerekmiyorsa, parametrenin / dizinin işlevinin ne olduğuna bağlı olarak değişir, daha az zaman alır mı? - Relic
İlginçtir ki dizi olmadan bile, çağrı hala daha hızlıdır. jsperf.com/applyvscallvsfn2 - Josh Mc
@JoshMc Bu çok tarayıcıya özgü olurdu. IE 11'de, iki kat daha hızlı arama yapmak için başvuruyorum. - Vincent McNabb
1. Yeni bir dizi oluşturmak, çöp toplayıcının bir noktada temizlemesinin gerekeceği anlamına gelir. 2. Dizideki öğelere erişim, dereferans kullanılarak doğrudan bir değişkene (parametreye) erişmekten daha az etkilidir. (Bence bu, "ayrıştırma" ile kastedilen, "aslında ayrıştırma" ile kastedilen şeydir.) Fakat argümanlarımın hiçbiri jsperf'i açıklamıyor. Bu, motorun iki fonksiyonun uygulanması ile ilgili olmalıdır, örn. Belki hiç geçilmemişse boş bir dizi yaratırlar. - joeytwiddle
Testi ve videoyu paylaştığınız için teşekkür ederiz - Gary


Bir özü takip eder Kapanış: Michael Bolin tarafından kesin bir rehber. Biraz uzun görünebilir, ama çok içgörü ile doymuş. "Ek B. Sıkça Yanlış Anlaşılan JavaScript Kavramları" dan:


Ne this Bir Fonksiyon Çağrıldığında Anlamına Gelir

Formun bir işlevini çağırırken foo.bar.baz(), nesne foo.bar alıcı olarak adlandırılır. Fonksiyon çağrıldığında, bu değer için kullanılan alıcıdır. this:

var obj = {};
obj.value = 10;
/** @param {...number} additionalValues */
obj.addValues = function(additionalValues) {
  for (var i = 0; i < arguments.length; i++) {
    this.value += arguments[i];
  }
  return this.value;
};
// Evaluates to 30 because obj is used as the value for 'this' when
// obj.addValues() is called, so obj.value becomes 10 + 20.
obj.addValues(20);

Bir fonksiyon çağrıldığında açık bir alıcı yoksa, global nesne alıcı olur. "Goog.global" sayfa 47'de açıklandığı gibi, bir web tarayıcısında JavaScript yürütüldüğünde pencere genel nesnedir. Bu bazı şaşırtıcı davranışlara yol açar:

var f = obj.addValues;
// Evaluates to NaN because window is used as the value for 'this' when
// f() is called. Because and window.value is undefined, adding a number to
// it results in NaN.
f(20);
// This also has the unintentional side effect of adding a value to window:
alert(window.value); // Alerts NaN

Buna rağmen obj.addValues ve f Aynı işleve başvururlar, çağrıldığında farklı davranırlar çünkü her bir çağrıda alıcının değeri farklıdır. Bu sebepten dolayı, bir işlev çağrıldığında thisemin olmak önemlidir this çağrıldığında doğru değere sahip olacaktır. Açık olmak gerekirse this işlev gövdesinde başvurulan değildi, sonra davranışını f(20) ve obj.addValues(20) aynı olurdu.

İşlevler, JavaScript'teki birinci sınıf nesneler olduğundan, kendi yöntemlerine sahip olabilirler. Tüm fonksiyonlar yöntemleri var call() ve apply() alıcıyı yeniden tanımlamayı mümkün kılar (ör. this işlevi çağırırken ifade eder. Yöntem imzaları aşağıdaki gibidir:

/**
* @param {*=} receiver to substitute for 'this'
* @param {...} parameters to use as arguments to the function
*/
Function.prototype.call;
/**
* @param {*=} receiver to substitute for 'this'
* @param {Array} parameters to use as arguments to the function
*/
Function.prototype.apply;

Arasındaki tek fark dikkat edin call() ve apply() bu mu call() fonksiyon parametrelerini bireysel argümanlar olarak alır, apply() onları tek bir dizi olarak alır:

// When f is called with obj as its receiver, it behaves the same as calling
// obj.addValues(). Both of the following increase obj.value by 60:
f.call(obj, 10, 20, 30);
f.apply(obj, [10, 20, 30]);

Aşağıdaki çağrılar eşdeğerdir f ve obj.addValues aynı işleve başvurunuz:

obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);

Ancak, ne de call() ne de apply() belirsiz olduğunda alıcı argümanının yerini almak için kendi alıcının değerini kullanır, aşağıdakiler işe yaramaz:

// Both statements evaluate to NaN
obj.addValues.call(undefined, 10, 20, 30);
obj.addValues.apply(undefined, [10, 20, 30]);

Değeri this asla olamaz null veya undefined Bir işlev çağrıldığında. Ne zaman null veya undefined alıcı olarak verilir call() veya apply(), global nesne yerine alıcı için değer olarak kullanılır. Bu nedenle, önceki kod, adında bir özellik eklemenin aynı istenmeyen yan etkisine sahiptir. value küresel nesneye

Bir işlevin, atanmış olan değişken hakkında hiçbir bilgiye sahip olmadığını düşünmek yararlı olabilir. Bu, işlev tanımlandığında değil, işlev çağrıldığında bunun değerinin sınırlandırılacağı fikrini güçlendirmeye yardımcı olur.


Ekstrenin sonu.


71
2017-12-04 12:41



Sadece gerçeğini not etmek için additionalValues içeride başvuruda bulunmuyor obj.addValues vücut - Viktor Stolbin
Soruyu cevapladığınızı biliyorum, ancak eklemek istiyorum: f'yi tanımlarken bağlayıcı kullanmış olabilirsiniz. var f = obj.addValues; olur var f = obj.addValues.bind(obj)   ve şimdi f (20) çağrı kullanmak zorunda kalmadan veya her seferinde uygulamak zorunda kalmadan çalışır. - jhliberty


Bir nesnenin başka bir nesnenin işlevini ödünç alması zaman zaman faydalıdır, bu da borçlanma nesnesinin borç verme işlevini kendisininki gibi yürütmesi anlamına gelir.

Küçük bir kod örneği:

var friend = {
    car: false,
    lendCar: function ( canLend ){
      this.car = canLend;
 }

}; 

var me = {
    car: false,
    gotCar: function(){
      return this.car === true;
  }
};

console.log(me.gotCar()); // false

friend.lendCar.call(me, true); 

console.log(me.gotCar()); // true

friend.lendCar.apply(me, [false]);

console.log(me.gotCar()); // false

Bu yöntemler, nesneler geçici işlevsellik vermek için çok kullanışlıdır.


33
2018-02-25 19:31



Görmeyi öğrenmek isteyen insanlara console.log Çıkış yapmak: Console.log nedir ve nasıl kullanırım? - Michel Ayres


Call, Apply ve Bind ile başka bir örnek. Çağrı ve Başvuru arasındaki fark açıktır, ancak bağlamak böyle çalışır:

  1. Bind, yürütülebilen bir işlev örneğini döndürür
  2. İlk Parametre 'bu'
  3. İkinci parametre bir Virgülle ayrılmış argümanlar listesi (gibi Aramak)

}

function Person(name) {
    this.name = name; 
}
Person.prototype.getName = function(a,b) { 
     return this.name + " " + a + " " + b; 
}

var reader = new Person('John Smith');

reader.getName = function() {
   // Apply and Call executes the function and returns value

   // Also notice the different ways of extracting 'getName' prototype
   var baseName = Object.getPrototypeOf(this).getName.apply(this,["is a", "boy"]);
   console.log("Apply: " + baseName);

   var baseName = Object.getPrototypeOf(reader).getName.call(this, "is a", "boy"); 
   console.log("Call: " + baseName);

   // Bind returns function which can be invoked
   var baseName = Person.prototype.getName.bind(this, "is a", "boy"); 
   console.log("Bind: " + baseName());
}

reader.getName();
/* Output
Apply: John Smith is a boy
Call: John Smith is a boy
Bind: John Smith is a boy
*/

23
2018-03-31 07:32