Chamadas de função
Funções são apenas um tipo de objeto.
Todos os objetos Function têm métodos de chamada e aplicação que executam o objeto Function em que são chamados.
Quando chamado, o primeiro argumento para esses métodos especifica o objeto que será referenciado pela this
palavra - chave durante a execução da Função - se for null
ou undefined
, o objeto global window
, for usado this
.
Assim, chamando uma função ...
whereAmI = "window";
function foo()
{
return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}
... entre parênteses - foo()
- é equivalente a foo.call(undefined)
ou foo.apply(undefined)
, que é efetivamente o mesmo que foo.call(window)
ou foo.apply(window)
.
>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"
Argumentos adicionais call
são passados como argumentos para a chamada de função, enquanto um único argumento adicional para apply
pode especificar os argumentos para a chamada de função como um objeto semelhante a uma matriz.
Assim, foo(1, 2, 3)
é equivalente a 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"
Se uma função é uma propriedade de um objeto ...
var obj =
{
whereAmI: "obj",
foo: foo
};
... acessar uma referência à Função por meio do objeto e chamá-la entre parênteses - obj.foo()
- é equivalente a foo.call(obj)
ou foo.apply(obj)
.
No entanto, funções mantidas como propriedades de objetos não são "vinculadas" a esses objetos. Como você pode ver na definição obj
acima, como Funções são apenas um tipo de Objeto, elas podem ser referenciadas (e, portanto, podem ser passadas por referência a uma chamada de Função ou retornadas por referência de uma chamada de Função). Quando uma referência a uma função é passado, nenhuma informação adicional sobre onde ele foi passado de é feita com ele, razão pela qual acontece o seguinte:
>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"
A chamada para nossa referência de função,, baz
não fornece nenhum contexto para a chamada, portanto é efetivamente o mesmo que baz.call(undefined)
, portanto, this
acaba fazendo referência window
. Se queremos baz
saber a que pertence obj
, precisamos fornecer de alguma forma essas informações quando baz
são chamadas, e é aí que o primeiro argumento para call
ou apply
e fechamentos entra em jogo.
Cadeias de escopo
function bind(func, context)
{
return function()
{
func.apply(context, arguments);
};
}
Quando uma Função é executada, ela cria um novo escopo e faz referência a qualquer escopo anexo. Quando a função anônima é criada no exemplo acima, ela tem uma referência ao escopo em que foi criada, que é bind
o escopo. Isso é conhecido como "fechamento".
[global scope (window)] - whereAmI, foo, obj, baz
|
[bind scope] - func, context
|
[anonymous scope]
Quando você tenta acessar uma variável, essa "cadeia de escopo" é direcionada para encontrar uma variável com o nome fornecido - se o escopo atual não contiver a variável, você olha para o próximo escopo na cadeia e assim por diante até chegar o escopo global. Quando a função anônima é retornada e bind
termina a execução, a função anônima ainda tem uma referência ao bind
escopo de tal modo que bind
o escopo não "desaparece".
Dado todo o exposto, agora você deve entender como o escopo funciona no exemplo a seguir e por que a técnica para passar uma função em torno de "pré-vinculado" com um valor específico this
dela terá quando for chamada de obras:
>>> 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); }};