Segue um extrato de Closure: The Definitive Guide, de Michael Bolin . Pode parecer um pouco demorado, mas está saturado de muita percepção. Do "Apêndice B. Conceitos de JavaScript frequentemente incompreendidos":
O que this
se refere quando uma função é chamada
Ao chamar uma função do formulário foo.bar.baz()
, o objeto foo.bar
é chamado de receptor. Quando a função é chamada, é o receptor que é usado como o 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);
Se não houver um receptor explícito quando uma função é chamada, o objeto global se torna o receptor. Conforme explicado em "goog.global" na página 47, window é o objeto global quando o JavaScript é executado em um navegador da web. Isso leva a um comportamento surpreendente:
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
Mesmo assim, obj.addValues
e se f
referem à mesma função, eles se comportam de maneira diferente quando chamados, porque o valor do receptor é diferente em cada chamada. Por esse motivo, ao chamar uma função que se refere this
, é importante garantir que this
o valor correto seja chamado. Para ser claro, se this
não fosse referenciado no corpo da função, o comportamento de f(20)
e obj.addValues(20)
seria o mesmo.
Como as funções são objetos de primeira classe em JavaScript, elas podem ter seus próprios métodos. Todas as funções possuem os métodos call()
e apply()
que permitem redefinir o receptor (ou seja, o objeto a que this
se refere) ao chamar a função. As assinaturas de método são as seguintes:
/**
* @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;
Observe que a única diferença entre call()
e apply()
é que call()
recebe os parâmetros da função como argumentos individuais, enquanto os apply()
recebe como uma ú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]);
As seguintes chamadas são equivalentes, como f
e obj.addValues
se referem à mesma função:
obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);
No entanto, como call()
nem apply()
usa o valor de seu próprio receptor para substituir o argumento do receptor quando não for especificado, o seguinte não funcionará:
// Both statements evaluate to NaN
obj.addValues.call(undefined, 10, 20, 30);
obj.addValues.apply(undefined, [10, 20, 30]);
O valor de this
nunca pode ser null
ou undefined
quando uma função é chamada. Quando null
ou undefined
é fornecido como receptor para call()
ou apply()
, o objeto global é usado como o valor para o receptor. Portanto, o código anterior tem o mesmo efeito colateral indesejável de adicionar uma propriedade nomeada value
ao objeto global.
Pode ser útil pensar em uma função como não tendo conhecimento da variável à qual está atribuída. Isso ajuda a reforçar a ideia de que o valor disso será vinculado quando a função for chamada e não quando for definida.
Fim da extração.
a
em candidatar-se a matriz de argumentos ec
em chamar por colunas de argumentos.