Exceções do this em Javascript
Fala galera, beleza? Visto que o no último post falei que o this
em javascript tem algumas exceções, vou falar sobre elas hoje.
Exceções relacionadas com o this
Como costume, toda regra tem exceções. E não é diferente para as regras que vimos no post anterior. Alguns cenários o comportamento do this
pode te deixar surpreso, onde você acredita que está sendo atribuido de uma forma mas acaba que a atribuição do this
sai um pouco diferente do planejado. Vamos analisar esses casos.
this
ignorado
Se passarmos null
ou undefined
como parâmetro do call, apply ou o bind, esses valores serão ignorados e o binding
default vai ser aplicado. Vamos ver um exemplo:
function teste() {
console.log( this.a );
}
var a = 2;
teste.call( null ); // 2
this
seguro
Talvez uma prática “segura” seja passar especificamente um objeto para inicialização para o this
que seja garantido não ser um objeto que crie efeitos colaterais no nosso programa. Nada mais especial do que um objeto completamente vazio
Onde quer que seja a chamada, a maneira mais fácil de criar um objeto totalmente vazio é Object.create(null)
. Object.create(null)
é similar ao { }
, mas sem o delegation
para Object.prototype
, logo é um vazio “mais vazio” que somente o { }
. Vamos ver um exemplo:
function foo(a,b) {
console.log( "a:" + a + ", b:" + b );
}
var vazio = Object.create( null );
foo.apply( ø, [2, 3] ); // a:2, b:3
var bar = foo.bind( vazio, 2 );
bar( 3 ); // a:2, b:3
Referências indiretas
Outra coisa que precisamos estar cientes é que você pode( intencionalmente ou não) criar referências indiretas a funções, e nesses casos, quando a referência é invocada, o default binding
também é aplicado.
Uma das maneiras mais comuns de referências indiretas ocorre da seguinte forma:
function teste() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, teste: teste };
var p = { a: 4 };
o.teste(); // 3
(p.teste = o.teste)(); // 2
O resultado dessa expressão p.foo = o.foo
é uma referência para o objeto da função subjacente. Dessa maneira, o call-site
, ou seja, o ponto de chamada é apenas foo()
, e não p.foo()
ou o.foo()
como você pode esperar. Pelas regras acima, o default binding
é aplicado.
this
Léxico
Funções normais continuam com as 4 regras que cobrimos no post anterior. Mas ES6 introduziu um tipo especial de função que não usa essas regras: arrow-functions
.
Arrow-functions
são sinalizadas não pelo palavra-chave function
, mas por =>
. No lugar de usar as quatros regras padrões, arrow-functions
adotam a atribuição do this
baseando-se no escopo delimitador(função ou global). Vamos ilustrar:
function foo() {
// retorna uma arrow function
return (a) => {
// `this` aqui é lexicamente adotado de `foo()`
console.log( this.a );
};
}
var obj1 = {
a: 2
};
var obj2 = {
a: 3
};
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2, not 3!
A arrow-function
criada em foo()
lexicamente captura qualquer que seja o this
de foo()
. Desde que foo()
teve o this
como obj1
, bar
vai também ter o this
como o obj1
. O binding
léxico da função arco não pode ser sobrescrito, mesmo com o new
!.
O caso mais comum de uso será no uso de callbacks, tais como event handlers
ou timers:
function foo() {
setTimeout(() => {
// `this` aqui é lexicamente adotado de foo
console.log( this.a );
},100);
}
var obj = {
a: 2
};
foo.call( obj ); // 2
Enquanto funções arco nos dão uma alternativa com relação à usar o bind(..)
na função para garantir o valor do this
, o que pode ser atraente, é importante notar que dessa forma estamos basicamente desabilitando o mecanismo do this
em favor de um escopo léxico mais amplamente entendido. Antes do EcmaScript 6 nós tinhamos como fazer esse padrão, que é quase indistinguível do espírito das funções arco do Ecmascript 6:
function foo() {
var self = this; // captura léxica do `this
setTimeout( function(){
console.log( self.a );
}, 100 );
}
var obj = {
a: 2
};
foo.call( obj ); // 2
Bom pessoal, acho que é isso. São muitos detalhes a aprender e espero que tenham gostado. Acredito que irei continuar escrevendo sobre javascript e nos vemos no próximo post. Abraço!