Ponteiro Javascript “this” na função aninhada

94

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

Digamos que eu insira este código de exemplo a seguir 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 naquela função aninhada, o ponteiro "this" está na verdade apontando para o objeto "janela" global - o que eu não esperava. Não devo estar entendendo algo corretamente porque pensei que, como declarei a função aninhada dentro de uma função do objeto, ela deveria ter um escopo "local" em relação à função (ou seja, o ponteiro "this" estaria se referindo ao próprio objeto como como é na minha primeira declaração "if").

Quaisquer sugestões (sem trocadilhos) seriam apreciadas.

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();
JoJoeDad
fonte
Qual é a tua pergunta exatamente ?
Sarfraz
2
Quando usado dentro de uma função, thisrefere-se ao objeto no qual a função é chamada.
aproximadamente
8
O que você poderia fazer no escopo externo é algo como var self = this;e, em seguida, referir-se à selffunção interna por meio de fechamento.
Kai
1
doSomeEffectsnão está associado a nenhum obj em particular, por isso thisé considerada a janela, a mãe de todos os elementos.
aproximadamente
3
@JoJoeDad Como posso dizer isso diplomaticamente? Mas a resposta dada por chuckj abaixo é de longe a resposta à sua pergunta. Para realmente entender o que está acontecendo, você deve ler o contexto de execução , a cadeia de escopo e esta palavra-chave . E pela aparência de algumas das respostas aqui, outras pessoas também deveriam lê-las. Tento evangelizar um bom javascript. É por isso que estou dedicando algum tempo para fornecer esses links.
onefootswill

Respostas:

120

Em JavaScript, o thisobjeto é realmente baseado em como você faz suas chamadas de função.

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

  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 thisobjeto será someThing. Chamar uma função sem um objeto pai principal geralmente obterá o objeto global que, na maioria dos navegadores, significa o windowobjeto.

KylePDavis
fonte
Mudei o código para com this.doSomeEffects();base na sua resposta, mas ainda não funciona. Por quê?
Arashsoft
1
@Arashsoft thisem this.doSomeEffects()pontos para std_obj. Conforme explicado na resposta acima, se uma função não tiver referência de objeto, ela thisserá considerada um objeto de janela.
Shivam
38

Uma vez que esta parece ser uma das questões mais votadas desse tipo, deixe-me adicionar, após todos esses anos, a solução ES6 usando funções de seta:

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") { }
    };
    ...    
  }
};

fonte
5
Agora isso é progresso!
Joshua Pinter
Excelente solução simples.
Joshua Schlichting de
32

thisnão faz parte do escopo de fechamento, pode ser considerado um parâmetro adicional para a função que está vinculada ao local da chamada. Se o método não for chamado como um método, o objeto global será 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 do método, como,

obj.someFunciton();

então thisestá ligado aobj .

Se você chamar someFunction () diretamente, como,

someFunction();

então thisestá ligado ao objeto global, isto éwindow .

A solução mais comum é capturar isso no fechamento, 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();         
 }      
chuckj
fonte
que = isso é perfeito
Nather Webber
10

Há uma diferença entre variáveis ​​de fechamento e "this". "this" é realmente definido pelo invocador da função, enquanto as variáveis ​​explícitas permanecem intactas dentro do bloco de declaração de função conhecido como anexo. 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 experimentar aqui: http://jsfiddle.net/kSTBy/

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

Shane
fonte
4
para os preguiçosos e impacientes, esta demonstração registra:_this.name = myFirstObject this.name = mySecondObject
ptim
9

Para entender esta questão, tente obter a saída para o seguinte snippet

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 irá gerar 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, podem referenciar e acessar foo corretamente.

Na função interna, porém, isso não se refere mais a meuObjeto. 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, isso na função interna se referia ao objeto de janela global; ao passo que, a partir do ECMA 5, isso na função interna seria indefinido.)

ronakshah725
fonte
1
Na função interna, thisrefere-se ao objeto janela (em um ambiente de navegador) ou objeto GLOBAL (em um ambiente node.js)
raneshu
2
Por que isso acontece?
setu
@raneshu "use estrito" e thisnão se refere mais a janela
rofrol
3

Conforme explicado por Kyle, você pode usar callou applypara especificar thisdentro da função:

Este é o conceito aplicado ao seu código:

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.apply(this,[]);
    }
};

std_obj.displayMe();

JsFiddle

Hari Pachuveetil
fonte
0

Também recebi um aviso " Acesso de referência potencialmente inválido a um campo de classe por meio disso "

class MyListItem {
    constructor(labelPrm) {
        this._flagActive = false;
        this._myLabel = labelPrm;
        labelPrm.addEventListener('click', ()=>{ this.onDropdownListsElementClick();}, false);
    }

    get myLabel() {
        return this._myLabel
    }
    get flagActive(){
        return this._flagActive;
    }

    onDropdownListsElementClick(){console.log("Now'this'refers to the MyListItem itself")}}//end of the class
CodeToLife
fonte
0

Como não foi mencionado, mencionarei que usar .bind()é uma solução -


        doSomeEffects=doSomeEffect.bind(this);
        doSomeEffects();   
      }
    };

    std_obj.displayMe();

Aqui está um exemplo mais simples -

bad = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x() 
  } 
};
bad.z() // prints 'undefined'

good = { 
  name:'NAME', 
  z : function() { 
    function x() { console.log(this.name); }; 
    x=x.bind(this);
    x();
  } 
};
good.z() // prints 'NAME'

É verdade que usar uma função de seta =>parece mais simples e fácil para o programador. No entanto, deve-se ter em mente que um escopo léxico provavelmente exigirá mais trabalho em termos de processamento e memória para configurar e manter esse escopo léxico, em comparação com simplesmente associar uma função a thisum ponteiro via.bind() .

Parte do benefício de desenvolver classes em JS era fornecer um método para fazer this mais confiável o presente e disponível, para desviar da programação funcional e escopos lexicais e, assim, reduzir a sobrecarga.

De MDN

Considerações de desempenho Não é aconselhável criar funções desnecessariamente dentro de outras funções se os encerramentos não forem necessários para uma tarefa específica, pois isso afetará negativamente o desempenho do script em termos de velocidade de processamento e consumo de memória.

Craig Hicks
fonte