Pesquisa de site

Ponteiro "this" do Javascript dentro de função aninhada


Eu tenho uma pergunta sobre como o ponteiro "this" é tratado em um cenário de função aninhada.

Digamos que eu insira o seguinte código de exemplo em uma página da web. Recebo um erro quando chamo a função aninhada "doSomeEffects()". Verifiquei no Firebug e ele indica que quando estou nessa função aninhada, o ponteiro "this" na verdade está apontando para o objeto global "window" - o que eu não esperava. Eu devo não estar entendendo algo corretamente porque pensei que, como declarei a função aninhada dentro de uma função do objeto, ela deveria ter escopo "local" em relação à função (ou seja, o ponteiro "this" se referiria ao próprio objeto, assim como está na minha primeira declaração "if").

Qualquer dica (sem trocadilho intencional) seria apreciada.

var std_obj = {
  options : { rows: 0, cols: 0 },
  activeEffect : "none",
  displayMe : function() {

    // the 'this' pointer is referring to the std_obj
    if (this.activeEffect=="fade") { }

    var doSomeEffects = function() {

      // the 'this' pointer is referring to the window obj, why?
      if (this.activeEffect=="fade") { }

    }

    doSomeEffects();   
  }
};

std_obj.displayMe();

Resposta #1

Em JavaScript, o objeto this realmente depende de como você faz suas chamadas de função.

Em geral, existem três maneiras de configurar o objeto this:

  1. someThing.someFunction(arg1, arg2, argN)
  2. someFunction.call(someThing, arg1, arg2, argN)
  3. someFunction.apply(someThing, [arg1, arg2, argN])

Em todos os exemplos acima, o objeto this será someThing. Chamar uma função sem um objeto pai à frente geralmente fará com que você obtenha o objeto global, o que na maioria dos navegadores significa o objeto window.

Resposta #2

Como esta parece ser uma das perguntas mais votadas desse tipo, deixe-me adicionar, depois de todos esses anos, a solução em ES6 usando funções arrow:

var std_obj = {
  ...
  displayMe() {
    ...
    var doSomeEffects = () => {
                        ^^^^^^^    ARROW FUNCTION    
      // In an arrow function, the 'this' pointer is interpreted lexically,
      // so it will refer to the object as desired.
      if (this.activeEffect=="fade") { }
    };
    ...    
  }
};

Resposta #3

this não faz parte do escopo da closure, pode ser pensado como um parâmetro adicional para a função que é vinculado no local da chamada. Se o método não for chamado como um método, então o objeto global é passado como this. No navegador, o objeto global é idêntico a window. Por exemplo, considere a seguinte função,

function someFunction() {
}

e o seguinte objeto,

var obj = { someFunction: someFunction };

Se você chamar a função usando a sintaxe de método, como,

obj.someFunciton();

então this está vinculado a obj.

Se você chamar someFunction() diretamente, como,

someFunction();

então this está vinculado ao objeto global, ou seja, window.

A solução alternativa mais comum é capturar isso no closure, como,

displayMe : function() {      

    // the 'this' pointer is referring to the std_obj      
    if (this.activeEffect=="fade") { }      
    var that = this;  
    var doSomeEffects = function() {      

      // the 'this' pointer is referring to global
      // that, however, refers to the outscope this
      if (that.activeEffect=="fade") { }      
    }      

    doSomeEffects();         
 }      

Resposta #4

Para entender esta questão, tente obter a saída do seguinte trecho

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.log("outer func:  this.foo = " + this.foo);
        console.log("outer func:  self.foo = " + self.foo);
        (function() {
            console.log("inner func:  this.foo = " + this.foo);
            console.log("inner func:  self.foo = " + self.foo);
        }());
    }
};
myObject.func();

O código acima exibirá o seguinte no console:

outer func:  this.foo = bar
outer func:  self.foo = bar
inner func:  this.foo = undefined
inner func:  self.foo = bar

Na função externa, tanto this quanto self se referem a myObject e, portanto, ambos podem referenciar e acessar foo corretamente.

Na função interna, porém, isso não se refere mais a myObject. Como resultado, this.foo é indefinido na função interna, enquanto a referência à variável local self permanece no escopo e é acessível lá. (Antes do ECMA 5, this na função interna se referiria ao objeto global window; enquanto, a partir do ECMA 5, this na função interna seria indefinido.)

Resposta #5

Há uma diferença entre variáveis de fechamento e "this". "this" é na verdade definido pelo invocador da função, enquanto variáveis explícitas permanecem intactas dentro do bloco de declaração da função conhecido como fechamento. Veja o exemplo abaixo:

function myFirstObject(){
    var _this = this;
    this.name = "myFirstObject";
    this.getName = function(){
       console.log("_this.name = " + _this.name + " this.name = " + this.name);  
    }
}

function mySecondObject(){
    var _this = this;
    this.name = "mySecondObject";
    var firstObject = new myFirstObject();
    this.getName = firstObject.getName
}

var secondObject = new mySecondObject();
secondObject.getName();

você pode experimentá-lo aqui: http://jsfiddle.net/kSTBy/

O que está acontecendo na sua função é que "doSomeEffects()" está sendo chamado explicitamente, isso significa que o contexto ou o "this" da função é a window. Se "doSomeEffects" fosse um método de protótipo, por exemplo, this.doSomeEffects em, digamos, "myObject", então myObject.doSomeEffects() faria com que "this" fosse "myObject".

Artigos relacionados