Quelle est la différence entre utiliser call
et apply
pour appeler une fonction?
var func = function() {
alert('hello!');
};
func.apply();
vs func.call();
Existe-t-il des différences de performances entre les deux méthodes susmentionnées? Quand est-il préférable d'utiliser call
sur apply
et vice versa?
La différence est que apply
vous permet d'appeler la fonction avec arguments
sous forme de tableau; call
nécessite que les paramètres soient listés explicitement. Un mnémonique utile est "A pour a rray et C pour c omma. "
Voir la documentation de MDN sur apply et call .
Pseudo syntaxe:
theFunction.apply(valueForThis, arrayOfArgs)
theFunction.call(valueForThis, arg1, arg2, ...)
Il existe également, à partir de ES6, la possibilité de spread
le tableau à utiliser avec la fonction call
, vous pouvez voir les compatibilités ici .
Exemple de code:
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
K. Scott Allen a n bel écrit en la matière.
Fondamentalement, ils diffèrent par la façon dont ils gèrent les arguments de la fonction.
La méthode apply () est identique à call (), sauf que apply () nécessite un tableau en tant que second paramètre. Le tableau représente les arguments de la méthode cible. "
Alors:
// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);
Pour répondre à la question de savoir quand utiliser chaque fonction, utilisez apply
si vous ne connaissez pas le nombre d'arguments que vous allez transmettre, ou s'ils se trouvent déjà dans un tableau ou un objet semblable à un tableau (comme le arguments
objet pour transférer vos propres arguments, utilisez call
sinon, puisqu'il n'est pas nécessaire de placer les arguments dans un tableau.
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
Quand je ne passe aucun argument (comme votre exemple), je préfère call
puisque je appelle la fonction. apply
signifierait que vous appliquez la fonction aux arguments (non existants).
Il ne devrait y avoir aucune différence de performances, sauf peut-être si vous utilisez apply
et enveloppez les arguments dans un tableau (par exemple f.apply(thisObject, [a, b, c])
au lieu de f.call(thisObject, a, b, c)
). Je ne l'ai pas testé, il pourrait donc y avoir des différences, mais ce serait très spécifique à votre navigateur. Il est probable que call
soit plus rapide si vous n'avez pas déjà les arguments dans un tableau et que apply
est plus rapide si vous en avez.
Voici un bon mnémonique. UNEutilisations ppl UNErays et UNEprend toujours un ou deux arguments. Quand vous utilisez Ctout ce que vous devez Cmontez le nombre d'arguments.
Bien qu’il s’agisse d’un sujet ancien, je tenais simplement à souligner que .call est légèrement plus rapide que .apply. Je ne peux pas vous dire exactement pourquoi.
Voir jsPerf, http://jsperf.com/test-call-vs-apply/
[UPDATE!
]
Douglas Crockford mentionne brièvement la différence entre les deux, ce qui peut aider à expliquer la différence de performance ... http://youtu.be/ya4UHuXNygM?t=15m52s
Apply prend un tableau d'arguments, tandis que Call ne prend aucun ou plusieurs paramètres individuels! Ah ah!
.apply(this, [...])
.call(this, param1, param2, param3, param4...)
Suit un extrait de Closure: The Definitive Guide de Michael Bolin . Cela peut sembler un peu long, mais il est saturé de perspicacité. Dans "Annexe B. Concepts JavaScript souvent mal compris":
this
fait-on référence quand une fonction est appeléeLors de l'appel d'une fonction de la forme foo.bar.baz()
, l'objet foo.bar
est appelé le destinataire. Lorsque la fonction est appelée, c'est le récepteur qui est utilisé comme valeur pour 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);
S'il n'y a pas de destinataire explicite lorsqu'une fonction est appelée, l'objet global devient le destinataire. Comme expliqué dans "goog.global" à la page 47, window est l'objet global lorsque JavaScript est exécuté dans un navigateur Web. Cela conduit à un comportement surprenant:
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
Même si obj.addValues
et f
font référence à la même fonction, ils se comportent différemment à l'appel, car la valeur du destinataire est différente dans chaque appel. Pour cette raison, lorsque vous appelez une fonction faisant référence à this
, il est important de vous assurer que this
aura la bonne valeur lors de son appel. Pour être clair, si this
n'était pas référencé dans le corps de la fonction, le comportement de f(20)
et obj.addValues(20)
serait alors le même.
Comme les fonctions sont des objets de première classe en JavaScript, elles peuvent avoir leurs propres méthodes. Toutes les fonctions ont les méthodes call()
et apply()
qui permettent de redéfinir le récepteur (c’est-à-dire l’objet auquel this
fait référence) lors de l’appel de la fonction. Les signatures de méthode sont les suivantes:
/**
* @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;
Notez que la seule différence entre call()
et apply()
est que call()
reçoit les paramètres de la fonction sous forme d'arguments individuels, alors que apply()
les reçoit sous la forme d'un tableau unique:
// 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]);
Les appels suivants sont équivalents, car f
et obj.addValues
font référence à la même fonction:
obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);
Cependant, étant donné que ni call()
ni apply()
n'utilisent la valeur de son propre récepteur pour remplacer l'argument du destinataire lorsqu'il n'est pas spécifié, les opérations suivantes ne fonctionneront pas:
// Both statements evaluate to NaN
obj.addValues.call(undefined, 10, 20, 30);
obj.addValues.apply(undefined, [10, 20, 30]);
La valeur de this
ne peut jamais être null
ni undefined
quand une fonction est appelée. Lorsque null
ou undefined
est fourni en tant que récepteur à call()
ou apply()
, l'objet global est utilisé comme valeur pour le récepteur. Par conséquent, le code précédent a le même effet secondaire indésirable d'ajouter une propriété nommée value
à l'objet global.
Il peut être utile de penser qu’une fonction n’a aucune connaissance de la variable à laquelle elle est affectée. Cela contribue à renforcer l’idée que la valeur de celle-ci sera liée lors de l’appel de la fonction plutôt que lors de sa définition.
Fin de l'extrait.
Il est parfois utile qu'un objet emprunte la fonction d'un autre objet, ce qui signifie que l'objet emprunteur exécute simplement la fonction prêtée comme si elle était la sienne.
n petit exemple de code:
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
Ces méthodes sont très utiles pour donner aux objets une fonctionnalité temporaire.
Un autre exemple avec Call, Apply et Bind. La différence entre Call et Apply est évidente, mais Bind fonctionne comme suit:
}
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
*/
Je voudrais montrer un exemple, où l'argument 'valueForThis' est utilisé:
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]
** détails: http://es5.github.io/#x15.4.4.7 *
Call () prend des arguments séparés par des virgules, ex:
.call(scope, arg1, arg2, arg3)
et apply () prend un tableau d'arguments, ex:
.apply(scope, [arg1, arg2, arg3])
voici quelques exemples d'utilisation supplémentaires: http://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/
Depuis , les documents MDN de Function.prototype.apply () :
La méthode apply () appelle une fonction avec une valeur
this
donnée et des arguments fournis sous forme de tableau (ou d'objet semblable à un tableau).Syntaxe
fun.apply(thisArg, [argsArray])
Depuis , les documents MDN sur Function.prototype.call () :
La méthode call () appelle une fonction avec une valeur
this
donnée et des arguments fournis individuellement.Syntaxe
fun.call(thisArg[, arg1[, arg2[, ...]]])
De Function.apply et Function.call en JavaScript :
La méthode apply () est identique à call (), sauf que apply () nécessite un tableau en tant que second paramètre. Le tableau représente les arguments de la méthode cible.
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'
));
Voir aussi ce violon .
La différence fondamentale est que call()
accepte une liste d'arguments , tandis que apply()
accepte un tableau unique d'arguments .
Voici un petit article, j'ai écrit à ce sujet:
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"
La différence est que call()
prend les arguments de la fonction séparément et apply()
prend les arguments de la fonction dans un tableau.
Nous pouvons différencier les méthodes d'appel et d'application comme ci-dessous
CALL: Une fonction avec argument fournit individuellement. Si vous connaissez les arguments à transmettre ou s'il n'y en a pas, vous pouvez utiliser call.
APPLY: Appelle une fonction avec un argument fourni sous forme de tableau. Vous pouvez utiliser apply si vous ne savez pas combien d'arguments vont être passés à la fonction.
Il y a un avantage à utiliser apply sur call, nous n'avons pas besoin de changer le nombre d'arguments, mais nous pouvons aussi changer un tableau passé.
Il n'y a pas grande différence de performance. Mais nous pouvons dire que call est un peu plus rapide que comparé à appliquer car un tableau doit être évalué dans la méthode apply.
Appelez et appliquez les deux pour forcer la valeur this
lorsqu'une fonction est exécutée. La seule différence est que call
prend n+1
arguments où 1 représente this
et 'n' arguments
. apply
ne prend que deux arguments, l'un est this
l'autre est un tableau d'arguments.
L'avantage que je vois dans apply
par rapport à call
est que nous pouvons facilement déléguer un appel de fonction à une autre fonction sans trop d'effort;
function sayHello() {
console.log(this, arguments);
}
function hello() {
sayHello.apply(this, arguments);
}
var obj = {name: 'my name'}
hello.call(obj, 'some', 'arguments');
Observez la facilité avec laquelle nous avons délégué hello
à sayHello
en utilisant apply
, mais avec call
cela est très difficile à obtenir.
La différence entre ces méthodes est la façon dont vous voulez transmettre les paramètres.
"A pour tableau et C pour virgule" est un mnémonique pratique.
Même si call
et apply
réalisent la même chose, je pense qu’il existe au moins un endroit où vous ne pouvez pas utiliser call
mais vous ne pouvez utiliser que apply
. C'est à ce moment que vous souhaitez prendre en charge l'héritage et que vous souhaitez appeler le constructeur.
Voici une fonction qui vous permet de créer des classes qui prend également en charge la création de classes en étendant d’autres classes.
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
La principale différence est que, en utilisant call, nous pouvons modifier la portée et transmettre les arguments normalement, mais appliquer vous permet de l'appeler en utilisant des arguments sous forme de tableau (les transmettre sous forme de tableau). Mais en ce qui concerne ce qu'ils doivent faire dans votre code, ils sont assez similaires.
Bien que la syntaxe de cette fonction soit presque identique à celle de apply (), la différence fondamentale est que call () accepte une liste d'arguments, tandis que apply () accepte un seul tableau d'arguments.
Donc, comme vous le voyez, il n'y a pas une grande différence, mais il y a quand même des cas où nous préférons utiliser call () ou apply (). Par exemple, regardez le code ci-dessous, qui recherche le nombre le plus petit et le plus grand dans un tableau à partir de MDN, à l'aide de la méthode 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)
La différence principale réside donc dans la manière dont nous passons les arguments:
Call:
function.call(thisArg, arg1, arg2, ...);
Appliquer:
function.apply(thisArg, [argsArray]);
call()
et apply()
sont des méthodes qui se trouvent sur Function.prototype
. Par conséquent, ils sont disponibles sur chaque objet de fonction via la chaîne de prototypes. call()
et apply()
peuvent tous deux exécuter une fonction avec une valeur spécifiée de this
.
La principale différence entre call()
et apply()
est que vous devez y passer des arguments. Dans call()
et apply()
, vous transmettez comme premier argument l'objet que vous souhaitez utiliser comme valeur this
. Les autres arguments diffèrent de la manière suivante:
call()
vous devez entrer les arguments normalement (à partir du second argument)apply()
vous devez transmettre un tableau d'arguments.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
La valeur this
peut être délicate parfois en javascript. La valeur de this
déterminée lorsqu'une fonction est exécutée, pas lorsqu'une fonction est définie. Si notre fonction dépend d'un lien droit this
, nous pouvons utiliser call()
. et apply()
pour appliquer ce comportement. Par exemple:
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