tutorial - todo sobre javascript




¿Cuál es la diferencia entre llamar y aplicar? (14)

Resumen:

Tanto call() como apply() son métodos que se encuentran en Function.prototype . Por lo tanto, están disponibles en cada objeto de función a través de la cadena de prototipo. Tanto call() como apply() pueden ejecutar una función con un valor específico de this .

La principal diferencia entre call() y apply() es la forma en que tiene que pasarle argumentos. Tanto en call() como en apply() usted pasa como primer argumento el objeto que desea que tenga el valor como this . Los otros argumentos difieren de la siguiente manera:

  • Con call() tienes que poner los argumentos normalmente (comenzando desde el segundo argumento)
  • Con apply() tienes que pasar en una matriz de argumentos.

Ejemplo:

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

¿Por qué necesitaría usar estas funciones?

this valor puede ser complicado a veces en javascript. El valor de this determina cuando una función no se ejecuta cuando se define una función. Si nuestra función depende de un derecho, this enlace podemos usar call() y apply() para imponer este comportamiento. Por ejemplo:

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

¿Cuál es la diferencia entre usar call y apply para invocar una función?

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

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

¿Existen diferencias de rendimiento entre los dos métodos mencionados? ¿Cuándo es mejor usar call over apply y viceversa?


A pesar de que call y apply es lo mismo, creo que hay al menos un lugar donde no se puede usar la call pero solo se puede apply . Esto es cuando desea admitir la herencia y desea llamar al constructor.

Aquí hay una función que le permite crear clases que también admiten la creación de clases extendiendo otras clases.

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

Aquí hay una buena mnemotécnica. A pply usa A rrays y A lways toma uno o dos Argumentos. Cuando usa C, todo lo que tiene que hacer para calcular el número de argumentos.


Aquí hay una pequeña publicación que escribí sobre esto:

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"

De los documentos MDN en Function.prototype.apply () :

El método apply () llama a una función con un determinado valor y argumentos proporcionados como una matriz (o un objeto similar a una matriz).

Sintaxis

fun.apply(thisArg, [argsArray])

De los documentos MDN en Function.prototype.call () :

El método call () llama a una función con un determinado valor y argumentos proporcionados individualmente.

Sintaxis

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

Desde Function.apply y Function.call en JavaScript :

El método apply () es idéntico a call (), excepto que apply () requiere una matriz como segundo parámetro. La matriz representa los argumentos para el método de destino.

Ejemplo de código:

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'
));

Véase también este violín .


K. Scott Allen tiene una buena reseña al respecto.

Básicamente, difieren en cómo manejan los argumentos de la función.

El método apply () es idéntico a call (), excepto que apply () requiere una matriz como segundo parámetro. La matriz representa los argumentos para el método de destino ".

Asi que:

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

La diferencia es que call() toma los argumentos de la función por separado, y apply() toma los argumentos de la función en una matriz.


La diferencia es que la apply permite invocar la función con arguments como una matriz; call requiere que los parámetros se enumeren explícitamente. Un mnemotécnico útil es " A para un rray y C para c omma".

Ver la documentación de MDN en apply y call .

Pseudo sintaxis:

theFunction.apply(valueForThis, arrayOfArgs)

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

También existe, a partir de ES6, la posibilidad de spread la matriz para usar con la función de call , puede ver las compatibilidades here .

Código de muestra:

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


La principal diferencia es que, al usar la llamada, podemos cambiar el alcance y pasar los argumentos de manera normal, pero aplicar permite usarlos como una matriz (pasarlos como una matriz). Pero en términos de lo que deben hacer en su código, son bastante similares.

Si bien la sintaxis de esta función es casi idéntica a la de apply (), la diferencia fundamental es que call () acepta una lista de argumentos, mientras que apply () acepta una única matriz de argumentos.

Como puede ver, no hay una gran diferencia, pero aún hay casos en los que preferimos usar call () o apply (). Por ejemplo, mire el código a continuación, que encuentra el número más pequeño y más grande en una matriz de MDN, usando el método de aplicación:

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

Así que la principal diferencia es la forma en que pasamos los argumentos:

Llamada:

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

Aplicar:

function.apply(thisArg, [argsArray]);

Llamar y aplicar ambos se utilizan para forzar this valor cuando se ejecuta una función. La única diferencia es que la call toma n+1 argumentos donde 1 es this y 'n' arguments . apply toma solo dos argumentos, uno es el otro es la matriz de argumentos.

La ventaja que veo en la apply sobre call es que podemos delegar fácilmente una llamada de función a otra función sin mucho esfuerzo;

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

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

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

Observe la facilidad con la que sayHello hello a sayHello uso de sayHello , pero con " call es muy difícil de lograr.


Otro ejemplo con Call, Apply y Bind. La diferencia entre Call y Apply es evidente, pero Bind funciona así:

  1. Bind devuelve una instancia de una función que puede ser ejecutada
  2. El primer parámetro es " esto "
  3. El segundo parámetro es una lista de argumentos separados por comas (como 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
*/

Para responder a la parte sobre cuándo usar cada función, use apply si no conoce la cantidad de argumentos que pasará, o si ya están en una matriz o un objeto similar a una matriz (como el objeto de arguments para reenviar su propia argumentos. Utilice call contrario, ya que no hay necesidad de envolver los argumentos en una matriz.

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

Cuando no estoy pasando ningún argumento (como su ejemplo), prefiero call ya que estoy llamando a la función. apply implicaría que está aplicando la función a los argumentos (no existentes).

No debería haber diferencias de rendimiento, excepto si utiliza y ajusta los argumentos en una matriz (por ejemplo, f.apply(thisObject, [a, b, c]) lugar de f.call(thisObject, a, b, c) ). No lo he probado, por lo que podría haber diferencias, pero sería muy específico del navegador. Es probable que la call sea ​​más rápida si aún no tiene los argumentos en una matriz y la apply es más rápida si la tiene.


Si bien este es un tema antiguo, solo quería señalar que .call es un poco más rápido que .apply. No te puedo decir exactamente por qué.

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

[ UPDATE! ]

Douglas Crockford menciona brevemente la diferencia entre los dos, lo que puede ayudar a explicar la diferencia de rendimiento ... http://youtu.be/ya4UHuXNygM?t=15m52s

Apply toma una serie de argumentos, mientras que Call toma cero o más parámetros individuales. Ah hah

.apply(this, [...])

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


Sigue un extracto de Closure: The Definitive Guide por Michael Bolin . Puede parecer un poco largo, pero está saturado con mucha información. Del "Apéndice B. Conceptos de JavaScript frecuentemente mal entendidos":

A qué se refiere this cuando se llama una función

Cuando se llama a una función de la forma foo.bar.baz() , el objeto foo.bar se conoce como el receptor. Cuando se llama a la función, es el receptor el que se utiliza como valor para 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);

Si no hay un receptor explícito cuando se llama a una función, entonces el objeto global se convierte en el receptor. Como se explica en "goog.global" en la página 47, la ventana es el objeto global cuando se ejecuta JavaScript en un navegador web. Esto lleva a un comportamiento sorprendente:

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

Aunque obj.addValues y f refieren a la misma función, se comportan de manera diferente cuando se les llama porque el valor del receptor es diferente en cada llamada. Por esta razón, cuando se llama a una función que se refiere a this , es importante asegurarse de que tendrá el valor correcto cuando se llame. Para ser claros, si this no fuera referenciado en el cuerpo de la función, entonces el comportamiento de f(20) y obj.addValues(20) sería el mismo.

Debido a que las funciones son objetos de primera clase en JavaScript, pueden tener sus propios métodos. Todas las funciones tienen los métodos call() y apply() que permiten redefinir el receptor (es decir, el objeto al que se refiere) al llamar a la función. Las firmas del método son las siguientes:

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

Tenga en cuenta que la única diferencia entre call() y apply() es que call() recibe los parámetros de la función como argumentos individuales, mientras que apply() recibe como una única matriz:

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

Las siguientes llamadas son equivalentes, ya que f y obj.addValues refieren a la misma función:

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

Sin embargo, dado que ni call() ni apply() utilizan el valor de su propio receptor para sustituir el argumento del receptor cuando no está especificado, lo siguiente no funcionará:

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

El valor de this nunca puede ser null o undefined cuando se llama a una función. Cuando null o undefined se proporciona como el receptor para call() o apply() , el objeto global se utiliza como el valor para el receptor. Por lo tanto, el código anterior tiene el mismo efecto secundario indeseable de agregar una propiedad denominada value al objeto global.

Puede ser útil pensar que una función no tiene conocimiento de la variable a la que está asignada. Esto ayuda a reforzar la idea de que el valor de esto se vinculará cuando se llame a la función en lugar de cuando se defina.

Fin del extracto.







dynamic