Appels de fonction
Les fonctions ne sont qu'un type d'objet.
Tous les objets Function ont des méthodes d' appel et d' application qui exécutent l'objet Function auquel ils sont appelés.
Lorsqu'il est appelé, le premier argument de ces méthodes spécifie l'objet qui sera référencé par le this
mot clé lors de l'exécution de la fonction - si c'est null
ou undefined
, l'objet global window
, est utilisé pour this
.
Ainsi, appeler une fonction ...
whereAmI = "window";
function foo()
{
return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}
... avec des parenthèses - foo()
- est équivalent à foo.call(undefined)
ou foo.apply(undefined)
, ce qui est en fait identique à foo.call(window)
ou foo.apply(window)
.
>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"
Des arguments supplémentaires à call
sont passés comme arguments à l'appel de fonction, tandis qu'un seul argument supplémentaire à apply
peut spécifier les arguments de l'appel de fonction en tant qu'objet de type tableau.
Ainsi, foo(1, 2, 3)
est équivalent à foo.call(null, 1, 2, 3)
ou foo.apply(null, [1, 2, 3])
.
>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"
Si une fonction est une propriété d'un objet ...
var obj =
{
whereAmI: "obj",
foo: foo
};
... accéder à une référence à la Fonction via l'objet et l'appeler entre parenthèses - obj.foo()
- est équivalent à foo.call(obj)
ou foo.apply(obj)
.
Cependant, les fonctions détenues comme propriétés d'objets ne sont pas "liées" à ces objets. Comme vous pouvez le voir dans la définition obj
ci-dessus, puisque les fonctions ne sont qu'un type d'objet, elles peuvent être référencées (et peuvent donc être passées par référence à un appel de fonction ou renvoyées par référence à partir d'un appel de fonction). Lorsqu'une référence à une fonction est passée, aucune autre information sur l' endroit où il a été passé de se fait avec elle, ce qui explique pourquoi les éléments suivants se produit:
>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"
L'appel à notre référence Function baz
, ne fournit aucun contexte pour l'appel, il est donc en fait le même que baz.call(undefined)
, donc this
finit par faire référence window
. Si nous voulons baz
savoir à quoi il appartient obj
, nous devons d'une manière ou d'une autre fournir ces informations lors de l' baz
appel, c'est là que le premier argument de call
ou apply
et les fermetures entrent en jeu.
Chaînes de portée
function bind(func, context)
{
return function()
{
func.apply(context, arguments);
};
}
Lorsqu'une fonction est exécutée, elle crée une nouvelle étendue et a une référence à toute étendue englobante. Lorsque la fonction anonyme est créée dans l'exemple ci-dessus, elle fait référence à l'étendue dans laquelle elle a été créée, qui est bind
l'étendue de. C'est ce qu'on appelle une «fermeture».
[global scope (window)] - whereAmI, foo, obj, baz
|
[bind scope] - func, context
|
[anonymous scope]
Lorsque vous essayez d'accéder à une variable, cette "chaîne de portée" est parcourue pour trouver une variable avec le nom donné - si la portée actuelle ne contient pas la variable, vous regardez la portée suivante de la chaîne, et ainsi de suite jusqu'à ce que vous atteigniez la portée mondiale. Lorsque la fonction anonyme est renvoyée et bind
termine son exécution, la fonction anonyme a toujours une référence à bind
la portée de ', donc bind
la portée de' ne disparaît pas '.
Compte tenu de tout ce qui précède, vous devriez maintenant être en mesure de comprendre comment fonctionne la portée dans l'exemple suivant, et pourquoi la technique pour passer une fonction autour de "pré-lié" avec une valeur particulière this
aura quand elle sera appelée fonctionne:
>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"
var signup = { onLoadHandler:function(){ console.log(this); return Type.createDelegate(this,this._onLoad); }, _onLoad: function (s, a) { console.log("this",this); }};