Compreendendo a palavra-chave this do JavaScript

PorAngus Croll em

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/

Deixe um comentário! 7

7 comentários

Comentários

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Comentando como Anônimo

  1. 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í.

  2. 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!

  3. Muito confuso mesmo, acho que deveria reformular isso ai haha , eu tenho duvida sobre this e eu até entendi um pouco , mas ele entrou em termos muito tecnicos que , quem está como iniciante é muito dificil de entender… deveria ter uma feedback dos termos usados, mas vou guardar esse post ja que futuramente poderei entender mais sobre isso

leia mais
Este projeto é mantido e patrocinado pelas empresas: