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();

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.


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

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();         
 }      

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".


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.)


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


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

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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.