Llamadas a funciones
Las funciones son solo un tipo de objeto.
Todos los objetos de función tienen métodos de llamada y aplicación que ejecutan el objeto de función al que están llamados.
Cuando se llama, el primer argumento de estos métodos especifica el objeto al que hará referencia la this
palabra clave durante la ejecución de la función, si se usa null
o undefined
, el objeto global, window
para this
.
Por lo tanto, llamando a una función ...
whereAmI = "window";
function foo()
{
return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}
... con paréntesis - foo()
- es equivalente a foo.call(undefined)
o foo.apply(undefined)
, que es efectivamente el mismo que foo.call(window)
o foo.apply(window)
.
>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"
Los argumentos adicionales a call
se pasan como argumentos a la llamada a la función, mientras que un único argumento adicional aapply
puede especificar los argumentos para la llamada a la función como un objeto tipo Array.
Por lo tanto, foo(1, 2, 3)
es equivalente a foo.call(null, 1, 2, 3)
o 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 una función es propiedad de un objeto ...
var obj =
{
whereAmI: "obj",
foo: foo
};
... acceder a una referencia a la Función a través del objeto y llamarla entre paréntesis - obj.foo()
- es equivalente foo.call(obj)
ofoo.apply(obj)
.
Sin embargo, las funciones mantenidas como propiedades de los objetos no están "vinculadas" a esos objetos. Como puede ver en la definición de obj
arriba, dado que las funciones son solo un tipo de objeto, se pueden hacer referencia a ellas (y, por lo tanto, se pueden pasar por referencia a una llamada de función o devolverse por referencia desde una llamada de función). Cuando se pasa una referencia a una Función, no se lleva información adicional sobre el lugar desde el que se pasó, por lo que sucede lo siguiente:
>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"
La llamada a nuestra referencia de función, baz
no proporciona ningún contexto para la llamada, por lo que es efectivamente igual que baz.call(undefined)
, por lo que this
termina haciendo referencia window
. Si queremos baz
saber que pertenece obj
, de alguna manera debemos proporcionar esa información cuando baz
se llama, que es donde entra en juego el primer argumento call
o apply
cierre.
Cadenas de alcance
function bind(func, context)
{
return function()
{
func.apply(context, arguments);
};
}
Cuando se ejecuta una función, crea un nuevo ámbito y tiene una referencia a cualquier ámbito de inclusión. Cuando se crea la función anónima en el ejemplo anterior, tiene una referencia al ámbito en el que se creó, que es bind
el ámbito. Esto se conoce como un "cierre".
[global scope (window)] - whereAmI, foo, obj, baz
|
[bind scope] - func, context
|
[anonymous scope]
Cuando intente acceder a una variable, se recorre esta "cadena de alcance" para encontrar una variable con el nombre dado: si el alcance actual no contiene la variable, observe el siguiente alcance en la cadena, y así sucesivamente hasta llegar a El alcance global. Cuando se devuelve la función anónima y bind
termina de ejecutarse, la función anónima aún tiene una referencia al bind
alcance de la aplicación, por lo que bind
el alcance no "desaparece".
Dado todo lo anterior, ahora debería poder comprender cómo funciona el alcance en el siguiente ejemplo, y por qué la técnica para pasar una función alrededor de "pre-enlazado" con un valor particular de la this
misma tendrá lugar cuando se llama funciona:
>>> 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); }};