it-roy-ru.com

В чем разница между заявкой и заявкой?

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

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

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

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

2921
John Duff

Разница в том, что apply позволяет вам вызывать функцию с arguments в качестве массива; call требует явного перечисления параметров. Полезной мнемоникой является "A для a Rray и C для c omma. "

См. Документацию 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
3483
flatline

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

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

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

Так:

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

Чтобы ответить на часть о том, когда использовать каждую функцию, используйте 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 быстрее, если у вас есть.

152
Matthew Crumley

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

105
Joe

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

См. JsPerf, http://jsperf.com/test-call-vs-apply/


[UPDATE!]

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

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

.apply(this, [...])

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

91
kmatheny

Далее следует выдержка из Закрытие: Полное руководство Майкла Болина . Это может выглядеть немного длинным, но оно насыщено глубоким пониманием. Из "Приложения Б. Часто неверно понимаемые концепции 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);

Если нет явного получателя при вызове функции, тогда глобальный объект становится получателем. Как объясняется в "goog.global" на странице 47, окно - это глобальный объект, когда 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 к глобальному объекту.

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


Конец выписки.

75
Dominykas Mostauskis

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

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

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

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

33
tjacks3

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

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

}

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

Я хотел бы показать пример, где используется аргумент 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 *

23
user669677

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

.call(scope, arg1, arg2, arg3)

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

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

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

20
Mark Karwowski

Из документов MDN на Function.prototype.apply () :

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

Синтаксис

fun.apply(thisArg, [argsArray])

Из документов MDN на Function.prototype.call () :

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

Синтаксис

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

From Function.apply и Function.call в JavaScript :

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


Пример кода:

var doSomething = function() {
    var arr = [];
    for(i in arguments) {
        if(typeof this[arguments[i]] !== 'undefined') {
            arr.Push(this[arguments[i]]);
        }
    }
    return arr;
}

var output = function(position, obj) {
    document.body.innerHTML += '<h3>output ' + position + '</h3>' + JSON.stringify(obj) + '\n<br>\n<br><hr>';
}

output(1, doSomething(
    'one',
    'two',
    'two',
    'one'
));

output(2, doSomething.apply({one : 'Steven', two : 'Jane'}, [
    'one',
    'two',
    'two',
    'one'
]));

output(3, doSomething.call({one : 'Steven', two : 'Jane'},
    'one',
    'two',
    'two',
    'one'
));

Смотрите также эту скрипку .

19
John Slegers

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

11
Rakesh Kumar

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

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"
10
Dan

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

7
Sanjib Debnath

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

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

ПРИМЕНИТЬ: вызвать функцию с аргументом, представленным в виде массива. Вы можете использовать apply, если вы не знаете, сколько аргументов будет передано функции.

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

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

6
Praveen D

Вызов и применение обоих используются для принудительного использования значения 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');

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

5
Raghavendra

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

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

5
venkat7668

Хотя 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
4
Dhana Krishnasamy

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

Хотя синтаксис этой функции практически идентичен синтаксису метода 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)

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

[. .____] Call:

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

Применить:

function.apply(thisArg, [argsArray]);
4
Alireza

Резюме:

И call(), и apply() являются методами, расположенными в Function.prototype. Поэтому они доступны для каждого объекта функции через цепочку прототипов. И call(), и 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
3
Willem van der Veen