javascript объект - В чем разница между призывом и подачей заявки?




function js (17)

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

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

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

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


Answers

Резюме:

Оба метода 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

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

Это значение может быть сложным иногда в 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


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


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

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

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

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

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


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


Далее следует выдержка из Closure: The Definitive Guide от Michael Bolin . Это может показаться немного длинным, но оно насыщено большим пониманием. Из «Приложение 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);

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

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

Конец экстракта.


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

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

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

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

Синтаксис

fun.apply(thisArg, [argsArray])

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

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

Синтаксис

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

From Function.apply и Function.call в JavaScript :

Метод apply () идентичен вызову (), за исключением того, что 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'
));

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


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

  1. Bind возвращает экземпляр функции, которая может быть выполнена
  2. Первый параметр - это « это »
  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
*/

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

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

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

Так:

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

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

См. Документацию MDN по apply и call .

Псевдо-синтаксис:

theFunction.apply(valueForThis, arrayOfArgs)

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

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

Образец кода:

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


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

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

[ UPDATE! ]

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

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

.apply(this, [...])

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


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

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 но можете использовать его. Это то, когда вы хотите поддерживать наследование и хотите вызвать конструктор.

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

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

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

.call(scope, arg1, arg2, arg3)

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

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

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


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

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


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


"use strict"делает код JavaScript для работы в строгом режиме , что в основном означает, что все должно быть определено перед использованием. Основная причина использования строгого режима - избежать случайного глобального использования неопределенных методов.

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

"use strict"широко используется для использования в ECMA5, в ECMA6 он является частью JavaScript по умолчанию , поэтому его не нужно добавлять, если вы используете ES6.

Посмотрите на эти утверждения и примеры из MDN:

Директива «use strict» Директива
«use strict» является новой в JavaScript 1.8.5 (ECMAScript версии 5). Это не утверждение, а буквальное выражение, игнорируемое более ранними версиями JavaScript. Цель «use strict» - указать, что код должен быть выполнен в «строгом режиме». При строгом режиме вы не можете, например, использовать необъявленные переменные.

Примеры использования «use strict»:
Строгий режим для функций: Аналогично, чтобы вызвать строгий режим для функции, поставьте точное утверждение «use strict»; (или «использовать strict»;) в теле функции перед любыми другими утверждениями.

1) строгий режим в функциях

 function strict() {
     // Function-level strict mode syntax
     'use strict';
     function nested() { return 'And so am I!'; }
     return "Hi!  I'm a strict mode function!  " + nested();
 }
 function notStrict() { return "I'm not strict."; }

 console.log(strict(), notStrict());

2) строгий режим полного скрипта

'use strict';
var v = "Hi! I'm a strict mode script!";
console.log(v);

3) Присвоение невостребованному глобальному

'use strict';

// Assignment to a non-writable global
var undefined = 5; // throws a TypeError
var Infinity = 5; // throws a TypeError

// Assignment to a non-writable property
var obj1 = {};
Object.defineProperty(obj1, 'x', { value: 42, writable: false });
obj1.x = 9; // throws a TypeError

// Assignment to a getter-only property
var obj2 = { get x() { return 17; } };
obj2.x = 5; // throws a TypeError

// Assignment to a new property on a non-extensible object.
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = 'ohai'; // throws a TypeError

Вы можете узнать больше о MDN .





javascript performance function dynamic