Canais iMasters

Javascript

Compreendendo a palavra-chave this do JavaScript

O que você precisa saber

Todo contexto de execução tem um valor associado ao this chamado thisBinding, cujo tempo de vida é igual àquele do contexto de execução e cujo valor é constante. Existem três tipos de contexto de execução:

 1. Contexto Global

O this está ligado ao objeto global (janela de um browser).

alert(this); //window

2. Contexto de Função

Existem pelo menos cinco maneiras de invocar uma função. O valor do this depende do método de invocação.

a) Invocação a partir de uma função como propriedade (método)
O this é o valor base (inglês) da referência da propriedade, ou seja, o objeto dono da propriedade.

var a = {
b: function () {
return this;
}
};

a.b(); //a;

a['b'](); //a;

var c = {};
c.d = a.b;
c.d(); //c

b) Invocação a partir de uma função como variável
O this é o objeto global.

var a = {
b: function () {
return this;
}
};

var foo = a.b;
foo(); //window

var a = {
b: function () {
var c = function () {
return this;
};
return c();
}
};

a.b(); //window

O mesmo se aplica para funções invocadas assim que declaradas:

var a = {
b: function () {
return (function () {return this;}());
}
};

a.b(); //window

c) Invocação a partir de Function.prototype.call
O this é passado como o primeiro argumento.

d) Invocação a partir de Function.prototype.apply
O this é passado como primeiro argumento também.

var a = {
b: function () {
return this;
}
};

var d = {};

a.b.apply(d); //d

e) Invocação  como um construtor usando new
O this é o novo objeto criado.

var A = function () {
this.toString = function () {return "I'm an A"};
};

new A(); //"I'm an A"

3.  Contexto de Avaliação (eval)

O valor this é pego do valor do this do contexto de execução onde foi invocado o eval.

alert(eval('this==window')); //true - (except firebug, see above)

var a = {
b: function () {
eval('alert(this==a)');
}
};

a.b(); //true;

O que você pode querer saber

Esta seção explora o processo pelo qual o this recebe seu valor no contexto funcional usando o ECMA 5 262 (.pdf) como referência.

Vamos começar com a definição ECMAScript para this:

A palavra-chave this avalia o valor do ThisBinding do contexto de execução atual.

A partir de ECMA 5, 11.1.1


Como o ThisBinding é configurado?

Cada função define um método interno [[Call]] (ECMA 5, 13.2.1 [[Call]]) que passa os valores de invocação para o contexto de execução da função:

Os próximos passos sãoexecutados quando o controle entra no contexto de execução do código da função do objeto  F, o invocador forneceu thisValue, e o invocador forneceu a lista de argumentos: 

1. Se o código da função for estritamente código, configure o ThisBinding para thisValue.
2. Se o thisValue for nulo ou indefinido, configure o ThisBinding para objeto global.
3. Se o Type(thisValue) não for um Objeto, configure o ThisBinding para ToObject(thisValue).
4. Em outros casos, configure o ThisBinding para thisValue.

a partir de ECMA 5, 10.4.3 Entering Function Code (levemente editado)

Em outras palavras, o ThisBinding é associado à coerção do objeto do argumento abstrato thisValue, ou se o thisValue é indefinido, ao objeto global (a não ser que esteja sendo executado em um strict mode, no qual thisValue está ligado ao ThisBinding).

Então de onde vem o thisValue?

 Aqui vamos precisar voltar aos nossos cinco tipos de invocação de funções:

1. Invocação a partir de uma propriedade (método)
2. Invocação a partir de uma variável

Na linguagem ECMAScript, estas são Function Calls e apresentam dois componentes: um MemberExpression e uma lista Arguments.

1. Deixe que o ref seja oresultado da avaliação do MemberExpression.
2. Deixe que func seja GetValue(ref).
6. Se Type(ref) for Reference, então
a. Se IsPropertyReference(ref) for verdadeiro
i. Deixe que thisValue seja GetBase(ref).
b. Do contrário, a base do ref será Environment Record
i. Deixe o thisValue ser o resultado do chamado do ImplicitThisValue, método concreto do GetBase(ref).
8. Retorne o resultao ao chamar o método interno [[Call]] na função, fornecendo o  thisValue como o valor final e fornecendo a lista argList como os valores de argumentos.

A partir de ECMA 5, 11.2.3 Function Calls

Portanto, em essência, o thisValue se torna o baseValue (inglês) da expressão da função  (veja o passo 6, acima).

Onde a função é expressada como uma propriedade, o baseValue é identificador antecedendo o ponto (ou colchete).

foo.bar(); //o valor de thisValue é o objeto foo
foo['bar'](); //o valor thisValue é o objeto foo

var foo = {
bar: function () {
//(Comments apply to example invocation only)
//MemberExpression = foo.bar
//thisValue = foo
//ThisBinding = foo
return this;
}
};
foo.bar(); //foo

Para variáveis, o baseValue é o VariableObject (o “Environment Record” acima), que é um Declarative Environment Record. O ECMA 10.2.1.1 nos diz que o ImplcitThisValue do Declarative Environment Record (registro declarado do ambiente de execução) é indefinido.

var bar = function () {…};
bar();  //thisValue é undefined

Revisitando 10.4.3 Entering Function Code (veja acima), observamos que a não ser em método restrito, um thisValue indefinido resulta em um valor ThisBinding de um objeto global. Então, this, na invocação de uma função variável, será o objeto global.

var bar = function() {
//(Comments apply to example invocation only)
//MemberExpression = bar
//thisValue = undefined
//ThisBinding = global object (e.g. window)
return this
};
bar(); //window

3. Invocação a partir de Function.prototype.apply
4. Invocação a partir de Function.prototype.call

(especificações em 15.3.4.3 Function.prototype.apply  e 15.3.4.4 Function.prototype.call)

Essas seções descrevem como, em invocações call e apply, o valor real do argumento da função (p.e. primeiro argumento) é passado como o thisValue para o 10.4.3 Entering Function Code. (Note que isso difere do ECMA 3, no qual valores thisArg primitivos submetem-se a uma transformação toObject, e valores nulos ou indefinidos são convertidos para o objeto global – mas a diferença normalmente será insignificante, uma vez que o valor irá se submeter a transformações idênticas na invocação da função alvo (como vimos no 10.4.3 Entering Function Code))

5. Invocação  como um construtor usando new

Quando o método interno [[Construct]]para o objeto função F é invocado com uma possível lista vazia de argumentos, os seguintes passos devem ser seguidos:
1. Deixe o objeto ser um novo objeto criado nativo do ECMAScript.
8. Deixe o resultado ser o resultado do chamado [[Call]] da propriedade interna do F, fornecendo o objeto como o thisValue e fornecendo a lista de argumento passada dentro de [[Construct]] como argumentos.
10. Retorne o objeto.

A partir de ECMA 5, 13.2.2 [[Construct]]

Isso está bastante claro. Ao invocar o construtor com new, um objeto é criado que e associado ao thisValue. É também uma ruptura radical de qualquer outro uso do this.

Limpando a casa

Strict mode
No strict mode do ECMAScript, o thisValue não está coagido a um objeto. Um thisValue de valores nulos ou indefinidos não é convertido para o objeto global, e os valores primitivos não são convertidos a objetos “embrulhados”.

A função bind
A Function.prototype.bind é nova no ECMAScript 5, mas já será familiar para os usuários dos principais frameworks. Baseada no call/apply, ela permite que você antecipe o thisValue de um contexto de execução usando uma sintaxe simples. Isso é especialmente útil para lidar com handlers de eventos, por exemplo, uma função que será invocada pelo clique de um botão, onde o ThisBinding do handler será padronizado como o baseValue da propriedade sendo invocada – p.e. o elemento Button:

//Bad Example: fails because ThisBinding of handler will be button
var sorter = {
sort: function() {
alert('sorting');
},
requestSorting: function() {
this.sort();
}
}
$('sortButton').onclick = sorter.requestSorting;
//Good Example: sorter baked into ThisBinding of handler
var sorter = {
sort: function() {
alert('sorting');
},
requestSorting: function() {
this.sort();
}
}
$('sortButton').onclick = sorter.requestSorting.bind(sorter);

Texto original disponível em  http://javascriptweblog.wordpress.com/2010/08/30/understanding-javascripts-this/


Comente também

5 Comentários

The Eyes
The Eyes

Meio confuso e mal escrito este artigo ai viu :S, para novatos ta muito dificil de entender ....

Moacir
Moacir

Idem opinião acima.

Igor
Igor

Realmente, já começa pela primeira frase:

"Todo contexto de execução tem um ThisBinding associado, cujo lifespan é igual àquele do contexto de execução e cujo valor é constante."

Nossa, tive que reler umas 3x pra entrar na cabeça, não passei daí.

Angus Croll
Angus Croll

From the author: I'm sorry you found this article hard to understand. It is difficult to translate technical articles from English to Portugese without losing some meaning and readability. If you understand English you might want to read this version

http://javascriptweblog.wordpress.com/2010/08/30/understanding-javascripts-this/

Danilo Abranches
Danilo Abranches

O artigo realmente ficou um pouco confuso, principalmente nas questões teóricas. Mas sem dúvida, vale a pena a leitura. Quanto a forma de trabalhar com o this, entendi muito bem.

Muito legal!

Qual a sua opinião?

Comentários considerados ofensivos serão moderados.

Parceiros

IBM
PagSeguro
Internet Innovation
Dialhost
HostNet
Tecla
KingHost
DotStore
Dinamize