Javascript this
Chamada de Função Simples
Considere a seguinte função:
function foo() {
console.log("bar");
console.log(this);
}
foo(); // calling the function
Observe que estamos executando isso no modo normal, ou seja, o modo estrito não é usado.
Ao executar em um navegador, o valor de thisseria registrado como window. Isso ocorre porque windowé a variável global no escopo de um navegador da web.
Se você executar esse mesmo pedaço de código em um ambiente como o node.js, thisconsulte a variável global no seu aplicativo.
Agora, se executarmos isso no modo estrito adicionando a instrução "use strict";ao início da declaração da função, thisnão se referirá mais à variável global em nenhum dos ambientes. Isso é feito para evitar confusões no modo estrito. thisnesse caso, apenas registraria undefined, porque é isso que é, não está definido.
Nos seguintes casos, veríamos como manipular o valor de this.
Chamando uma função em um objeto
Existem diferentes maneiras de fazer isso. Se você tem chamado métodos nativos em Javascript como forEache slice, você já deve saber que a thisvariável, nesse caso, refere-se ao Objectem que você chamou essa função (Note-se que em javascript, quase tudo é um Object, incluindo Arrays e Functions). Pegue o seguinte código, por exemplo.
var myObj = {key: "Obj"};
myObj.logThis = function () {
// I am a method
console.log(this);
}
myObj.logThis(); // myObj is logged
Se um Objectcontém uma propriedade que contém a Function, a propriedade é chamada de método. Este método, quando chamado, sempre terá sua thisvariável definida como a Objectqual está associada. Isso vale para os modos estrito e não estrito.
Observe que se um método for armazenado (ou melhor, copiado) em outra variável, a referência a thisnão será mais preservada na nova variável. Por exemplo:
// continuing with the previous code snippet
var myVar = myObj.logThis;
myVar();
// logs either of window/global/undefined based on mode of operation
Considerando um cenário mais comumente prático:
var el = document.getElementById('idOfEl');
el.addEventListener('click', function() { console.log(this) });
// the function called by addEventListener contains this as the reference to the element
// so clicking on our element would log that element itself
A newpalavra-chave
Considere uma função construtora em Javascript:
function Person (name) {
this.name = name;
this.sayHello = function () {
console.log ("Hello", this);
}
}
var awal = new Person("Awal");
awal.sayHello();
// In `awal.sayHello`, `this` contains the reference to the variable `awal`
Como é que isso funciona? Bem, vamos ver o que acontece quando usamos a newpalavra - chave.
- Chamar a função com a
newpalavra - chave inicializaria imediatamente um Objecttipo Person.
- O construtor disso
Objecttem seu construtor definido como Person. Além disso, observe que typeof awalretornaria Objectapenas.
- A este novo
Objectseria atribuído o protótipo de Person.prototype. Isso significa que qualquer método ou propriedade no Personprotótipo estaria disponível para todas as instâncias de Person, inclusive awal.
- A função em
Personsi agora é invocada; thissendo uma referência ao objeto recém-construído awal.
Bem direto, não é?
Observe que a especificação oficial do ECMAScript em nenhum lugar afirma que esses tipos de funções são reais constructor. São apenas funções normais e newpodem ser usadas em qualquer função. É que nós os usamos como tal e, portanto, os chamamos apenas como tal.
Chamando funções em Funções: calleapply
Então sim, como functions também são Objects(e de fato variáveis de primeira classe em Javascript), até as funções têm métodos que são ... bem, elas próprias.
Todas as funções herdam do global Function, e dois de seus muitos métodos são calle apply, e ambos podem ser usados para manipular o valor da thisfunção na qual são chamados.
function foo () { console.log (this, arguments); }
var thisArg = {myObj: "is cool"};
foo.call(thisArg, 1, 2, 3);
Este é um exemplo típico de uso call. Basicamente, pega o primeiro parâmetro e define thisa função foocomo referência thisArg. Todos os outros parâmetros passados callsão passados para a função foocomo argumentos.
Portanto, o código acima fará logon {myObj: "is cool"}, [1, 2, 3]no console. Maneira bastante agradável de alterar o valor de thisem qualquer função.
applyé quase o mesmo que callaceitar que são necessários apenas dois parâmetros: thisArge uma matriz que contém os argumentos a serem passados para a função. Portanto, a callchamada acima pode ser traduzida para apply:
foo.apply(thisArg, [1,2,3])
Observe que calle applypode substituir o valor da thischamada de método definido por ponto que discutimos no segundo marcador. Simples o suficiente :)
Apresentando .... bind!
bindé irmão de calle apply. É também um método herdado por todas as funções do Functionconstrutor global em Javascript. A diferença entre binde call/ applyé que ambos calle applyrealmente chamarão a função. bind, por outro lado, retorna uma nova função com thisArge argumentspré-configurada. Vamos dar um exemplo para entender melhor isso:
function foo (a, b) {
console.log (this, arguments);
}
var thisArg = {myObj: "even more cool now"};
var bound = foo.bind(thisArg, 1, 2);
console.log (typeof bound); // logs `function`
console.log (bound);
/* logs `function () { native code }` */
bound(); // calling the function returned by `.bind`
// logs `{myObj: "even more cool now"}, [1, 2]`
Veja a diferença entre os três? É sutil, mas eles são usados de maneira diferente. Like calle apply, bindtambém substituem o valor de thisset pela chamada do método dot.
Observe também que nenhuma dessas três funções altera a função original. calle applyretornaria o valor das funções recém-construídas, enquanto bindretornaria a própria função recém-construída, pronta para ser chamada.
Coisas extras, copie isso
Às vezes, você não gosta do fato de que thismuda com o escopo, especialmente o escopo aninhado. Veja o exemplo a seguir.
var myObj = {
hello: function () {
return "world"
},
myMethod: function () {
// copy this, variable names are case-sensitive
var that = this;
// callbacks ftw \o/
foo.bar("args", function () {
// I want to call `hello` here
this.hello(); // error
// but `this` references to `foo` damn!
// oh wait we have a backup \o/
that.hello(); // "world"
});
}
};
No código acima, vemos que o valor de thismudou com o escopo aninhado, mas queríamos o valor do thisescopo original. Então nós 'copiado' thisa thate usou a copiar em vez de this. Inteligente, não é?
Índice:
- O que é mantido
thispor padrão?
- E se chamarmos a função como um método com a notação Objeto-ponto?
- E se usarmos a
newpalavra - chave?
- Como podemos manipular
thiscom calle apply?
- Usando
bind.
- Copiando
thispara resolver problemas de escopo aninhado.