Какова разница между и?

в чем разница между использованием call и apply для вызова функции?

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

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

есть ли разница в производительности между двумя вышеупомянутыми методами? Когда лучше всего использовать call над apply и наоборот?

20 ответов


разница в том, что apply позволяет вызывать функцию с помощью arguments как массив; call требует, чтобы параметры были перечислены явно. Полезная мнемосхема "A на array и C на cомма."

см. документацию MDN на применить и вызов.

псевдо синтаксис:

theFunction.apply(valueForThis, arrayOfArgs)

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

существует также, начиная с ES6, возможность spread массив для использования с call функция, вы можете увидеть совместимость здесь.

пример кода:

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

К. Скотт Аллен хорошая рецензия по этому вопросу.

в основном, они отличаются тем, как они обрабатывают аргументы функции.

метод apply () идентичен call (), за исключением того, что apply () требует массива в качестве второго параметра. Массив представляет аргументы для метода target."

Так:

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

чтобы ответить на часть о том, когда использовать каждую функцию, используйте apply Если вы не знаете количество аргументов вы будете проходить, или если они уже в массив или массив-как объект (например,arguments объект для пересылки ваших собственных аргументов. Использовать call в противном случае, так как нет необходимости обертывать аргументы в массиве.

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

когда я не передаю никаких аргументов (например, ваш пример), я предпочитаю call так как я вызов функции. apply будет означать, что вы применение функция для (несуществующих) аргументов.

не должно быть никаких различий в производительности, за исключением, возможно, если вы используете apply и оберните аргументы в массив (например,f.apply(thisObject, [a, b, c]) вместо f.call(thisObject, a, b, c)). Я не тестировал его, поэтому могут быть различия, но это будет очень специфично для браузера. Вполне вероятно, что call быстрее, если у вас еще нет аргументов в массиве и apply быстрее, если вы делаете.


вот хорошая мнемоника. наpply использует наrrays и навсегда принимает один или два аргумента. Когда вы используете Cвсе, что вам нужно Count количество аргументов.


хотя это старая тема, я просто хотел указать на это .вызов немного быстрее, чем .применять. Не могу сказать, почему.

см. jsPerf,http://jsperf.com/test-call-vs-apply/3


[UPDATE!]

Дуглас Крокфорд кратко упоминает разницу между ними, что может помочь объяснить разницу в производительности... http://youtu.be/ya4UHuXNygM?t=15m52s

Apply принимает массив аргументы, в то время как вызов принимает ноль или более отдельных параметров! Ах хах!

.apply(this, [...])

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


следует выдержка из закрытие: окончательное руководство Майкла Болина. Это может показаться немного длинным,но он насыщен большим пониманием. Из "Приложения B. Часто Неправильно Понимаемые Концепции JavaScript":


что this относится к тому, когда функция называется

при вызове функции вида foo.bar.baz() объект foo.bar называется приемник. Когда функция вызывается, это приемник, который используется как значение 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);

если при вызове функции нет явного приемника, глобальный объект становится приемником. Как пояснили в "хорошо.global " на странице 47 window является глобальным объектом при выполнении JavaScript в веб-браузере. Это приводит к некоторому удивительному поведению:

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

хотя obj.addValues и f обратитесь к той же функции, они ведут себя по-разному при вызове, потому что значение приемника отличается в каждом вызов. По этой причине при вызове функции, которая ссылается на this, важно обеспечить это this будет иметь правильное значение при вызове. Чтобы было понятно, если this не были указаны в теле функции, тогда поведение f(20) и obj.addValues(20) было бы то же самое.

поскольку функции являются первоклассными объектами в JavaScript, они могут иметь свои собственные методы. Все функции имеют методы call() и apply(), которые позволяют пересмотреть приемник (т. е. объект, который this относится к) при вызове функции. Сигнатуры метода следующие:

/**
* @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;

обратите внимание, что единственная разница между call() и apply() это call() получает параметры функции в виде отдельных аргументов, тогда как apply() получает их как единый массив:

// 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]);

следующие вызовы эквивалентны, так как f и obj.addValues обратитесь к той же функции:

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

однако, так как ни call(), ни apply() использует значение своего собственного приемника для замены аргумента приемника, когда он не указан, следующее не будет работать:

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

значение this не может быть null или undefined при вызове функции. Когда null или undefined поставляется в качестве приемника к call() или apply() глобальный объект используется в качестве значения, а не приемник. Таким образом, предыдущий код имеет тот же нежелательный побочный эффект добавления свойство с именем value к глобальному объекту.

может быть полезно думать о функции как не имеющей знания переменной, которой она назначена. Это помогает укрепить идею о том, что значение это будет связано, когда функция вызывается, а не когда он определен.


конец выдержки.


иногда полезно, чтобы один объект заимствовал функцию другого объекта, что означает, что объект заимствования просто выполняет одолженную функцию, как если бы она была его собственной.

небольшой пример кода:

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

эти методы очень полезны для предоставления объектов временной функциональности.


другой пример с Call, Apply и Bind. Разница между Call и Apply очевидна, но Bind работает так:

  1. Bind возвращает экземпляр функции, которая может быть выполнена
  2. первый параметр -'этой'
  3. второй параметр-это , разделенных запятой список аргументов (например,вызов)

}

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
*/

Я хотел бы показать пример, где используется аргумент valueForThis:

Array.prototype.push = function(element) {
   /*
   Native code*, that uses 'this'       
   this.put(element);
   */
}
var array = [];
array.push(1);
array.push.apply(array,[2,3]);
Array.prototype.push.apply(array,[4,5]);
array.push.call(array,6,7);
Array.prototype.push.call(array,8,9);
//[1, 2, 3, 4, 5, 6, 7, 8, 9] 

* * детали:http://es5.github.io/#x15.4.4.7*


Call () принимает аргументы, разделенные запятыми, например:

.call(scope, arg1, arg2, arg3)

и apply () принимает массив аргументов, например:

.apply(scope, [arg1, arg2, arg3])

вот еще несколько примеров использования: http://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/


С MDN docs on функция.прототип.apply () :

метод apply () вызывает функцию с заданным this значение и аргументы как массив (или массив-как объект).

синтаксис

fun.apply(thisArg, [argsArray])

С MDN docs on функция.прототип.call () :

метод call() вызывает функцию с заданным this значение и аргументы приводятся индивидуально.

синтаксис

fun.call(thisArg[, arg1[, arg2[, ...]]])

С


фундаментальное различие в том, что call() принимает список аргументов, а apply() принимает один массив аргументов.


вот небольшой пост, я написал на этом:

http://sizeableidea.com/call-versus-apply-javascript/

var obj1 = { which : "obj1" },
obj2 = { which : "obj2" };

function execute(arg1, arg2){
    console.log(this.which, arg1, arg2);
}

//using call
execute.call(obj1, "dan", "stanhope");
//output: obj1 dan stanhope

//using apply
execute.apply(obj2, ["dan", "stanhope"]);
//output: obj2 dan stanhope

//using old school
execute("dan", "stanhope");
//output: undefined "dan" "stanhope"

разница в том, что call() принимает аргументы функции отдельно и apply() принимает аргументы функции в массиве.


мы можем дифференцировать вызов и применять методы, как показано ниже

CALL: функция с аргументом предоставляет индивидуально. Если вы знаете аргументы для передачи или нет аргумента для передачи, вы можете использовать вызов.

APPLY: вызовите функцию с аргументом, предоставленным в виде массива. Вы можете использовать apply, если не знаете, сколько аргументов будет передано функции.

есть преимущество использования apply over call, нам не нужно менять номер аргумент только мы можем изменить массив, который передается.

нет большой разницы в производительности. Но мы можем сказать, что вызов немного быстрее по сравнению с apply, потому что массив нужно оценить в методе apply.


разница между этими методами, как вы хотите передать параметры.

" a для массива и C для запятой " - удобная мнемоника.


вызов и применить оба используются для принудительного this значение при выполнении функции. Разница только в том, что call принимает n+1 аргументы, где 1 this и 'n' arguments. apply принимает только два аргумента, один this другой-массив аргументов.

преимущество, которое я вижу в apply над call это то, что мы можем легко делегировать вызов функции другой функции без особых усилий;

function sayHello() {
  console.log(this, arguments);
}

function hello() {
  sayHello.apply(this, arguments);
}

var obj = {name: 'my name'}
hello.call(obj, 'some', 'arguments');

обратите внимание, как легко мы делегировали hello для sayHello используя apply, но с call этого очень трудно достичь.


хотя call и apply достичь того же самого, я думаю, что есть по крайней мере одно место, где вы не можете использовать call но может только использовать apply. То есть, когда вы хотите поддержать наследование и хотите вызвать конструктор.

здесь функция позволяет создавать классы, которые также поддерживают создание классов путем расширения других классов.

function makeClass( properties ) {
    var ctor = properties['constructor'] || function(){}
    var Super = properties['extends'];
    var Class = function () {
                 // Here 'call' cannot work, only 'apply' can!!!
                 if(Super)
                    Super.apply(this,arguments);  
                 ctor.apply(this,arguments);
                }
     if(Super){
        Class.prototype = Object.create( Super.prototype );
        Class.prototype.constructor = Class;
     }
     Object.keys(properties).forEach( function(prop) {
           if(prop!=='constructor' && prop!=='extends')
            Class.prototype[prop] = properties[prop];
     });
   return Class; 
}

//Usage
var Car = makeClass({
             constructor: function(name){
                         this.name=name;
                        },
             yourName: function() {
                     return this.name;
                   }
          });
//We have a Car class now
 var carInstance=new Car('Fiat');
carInstance.youName();// ReturnsFiat

var SuperCar = makeClass({
               constructor: function(ignore,power){
                     this.power=power;
                  },
               extends:Car,
               yourPower: function() {
                    return this.power;
                  }
              });
//We have a SuperCar class now, which is subclass of Car
var superCar=new SuperCar('BMW xy',2.6);
superCar.yourName();//Returns BMW xy
superCar.yourPower();// Returns 2.6

основное отличие заключается в том, что с помощью вызова мы можем изменить область и передать аргументы как обычно, но apply позволяет вызывать его с помощью аргументов в виде массива (передавать их как массив). Но с точки зрения того, что они должны делать в вашем коде, они довольно похожи.

в то время как синтаксис этой функции почти идентичен синтаксису apply (), фундаментальное различие заключается в том, что call () принимает аргумент список, в то время как apply() принимает один массив аргументы.

Итак, как вы видите, нет большой разницы, но все же есть случаи, когда мы предпочитаем использовать call () или apply (). Например, посмотрите на приведенный ниже код, который находит наименьшее и наибольшее число в массиве из MDN, используя метод apply:

// min/max number in an array
var numbers = [5, 6, 2, 3, 7];

// using Math.min/Math.max apply
var max = Math.max.apply(null, numbers); 
// This about equal to Math.max(numbers[0], ...)
// or Math.max(5, 6, ...)

var min = Math.min.apply(null, numbers)

таким образом, основное различие заключается в том, как мы передаем аргументы:

звоните:

function.call(thisArg, arg1, arg2, ...);

применение:

function.apply(thisArg, [argsArray]);

резюме:

и call() и apply() являются методы, которые расположены на Function.prototype. Поэтому они доступны на каждом объекте функции через цепочку прототипов. Оба!--2--> и apply() может выполнять функцию с заданным значением this.

основное различие между call() и apply() - это способ, которым вы должны передать в него аргументы. В обоих call() и apply() вы передаете в качестве первого аргумента объект, который вы хотите быть значение this. Остальные аргументы отличаются следующим образом:

  • С call() вы должны поместить аргументы нормально (начиная со второго аргумента)
  • С apply() вы должны передать массив аргументов.

пример:

let obj = {
  val1: 5,
  val2: 10
}

const summation = function (val3, val4) {
  return  this.val1 + this.val2 + val3 + val4;
}

console.log(summation.apply(obj, [2 ,3]));
// first we assign we value of this in the first arg
// with apply we have to pass in an array


console.log(summation.call(obj, 2, 3));
// with call we can pass in each arg individually

зачем мне использовать эти функции?

на this значение может быть сложно иногда в javascript. Значение this определены когда функция выполняется, а не когда функция определена. если наша функция зависит от права this привязку, мы можем использовать call() и apply() для реализации этого поведения. Например:

var name = 'unwantedGlobalName';

const obj =  {
  name: 'Willem',
  sayName () { console.log(this.name);}
}


let copiedMethod = obj.sayName;
// we store the function in the copiedmethod variable



copiedMethod();
// this is now window, unwantedGlobalName gets logged

copiedMethod.call(obj);
// we enforce this to be obj, Willem gets logged