Puntero "this" de JavaScript dentro de la función anidada

94

Tengo una pregunta sobre cómo se trata el puntero "this" en un escenario de función anidada.

Digamos que inserto el siguiente código de muestra en una página web. Recibo un error cuando llamo a la función anidada "doSomeEffects ()". Revisé Firebug e indica que cuando estoy en esa función anidada, el puntero "this" en realidad apunta al objeto de "ventana" global, lo cual no esperaba. No debo estar entendiendo algo correctamente porque pensé que desde que declaré la función anidada dentro de una función del objeto, debería tener un alcance "local" en relación con la función (es decir, el puntero "this" se referiría al objeto en sí como cómo está en mi primera declaración "si").

Se agradecería cualquier sugerencia (sin juego de palabras).

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
fuente
Cuál es exactamente su pregunta ?
Sarfraz
2
Cuando se usa dentro de una función, se thisrefiere al objeto en el que se invoca la función.
aproximadamente azul
8
Lo que podría hacer en el alcance externo es algo así como var self = this;y luego hacer referencia selfen la función interna a través del cierre.
Kai
1
doSomeEffectsno está asociado con ningún obj en particular, por lo que thisse supone que es la ventana, la madre de todos los elementos.
aproximadamente azul
3
@JoJoeDad ¿Cómo digo esto diplomáticamente? Pero la respuesta dada por chuckj a continuación es, con mucho, la respuesta a su pregunta. Para comprender realmente lo que está sucediendo, debe leer el contexto de ejecución , la cadena de alcance y esta palabra clave . Y por el aspecto de algunas de las respuestas aquí, otras personas también deberían leerlas. Intento evangelizar un buen javascript. Es por eso que me estoy tomando el tiempo para dar estos enlaces.
onefootswill

Respuestas:

120

En JavaScript, el thisobjeto se basa realmente en cómo realiza sus llamadas a funciones.

En general, hay tres formas de configurar el thisobjeto:

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

En todos los ejemplos anteriores, el thisobjeto será someThing. Llamar a una función sin un objeto principal principal generalmente le dará el objeto global que en la mayoría de los navegadores significa el windowobjeto.

KylePDavis
fuente
Cambié el código this.doSomeEffects();según su respuesta, pero aún así no funciona. ¿Por qué?
Arashsoft
1
@Arashsoft thisen this.doSomeEffects()apunta a std_obj. Como se explicó en la respuesta anterior, si una función no tiene una referencia de objeto, entonces thisdebe ser un objeto de ventana.
Shivam
38

Dado que esta parece ser una de las preguntas más votadas de su tipo, permítanme agregar, después de todos estos años, la solución ES6 usando funciones de flecha:

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

fuente
5
¡Eso es progreso!
Joshua Pinter
Excelente y simple solución.
Joshua Schlichting
32

thisno es parte del alcance de cierre, se puede considerar como un parámetro adicional a la función que está vinculada al sitio de la llamada. Si el método no se llama como método, el objeto global se pasa como this. En el navegador, el objeto global es idéntico a window. Por ejemplo, considere la siguiente función,

function someFunction() {
}

y el siguiente objeto,

var obj = { someFunction: someFunction };

Si llama a la función utilizando una sintaxis de método como,

obj.someFunciton();

entonces thisestá obligado a obj.

Si llama a someFunction () directamente, como,

someFunction();

luego thisestá vinculado al objeto global, es decir window.

La solución más común es capturar esto en el cierre, 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
fuente
eso = esto es perfecto
Nather Webber
10

Hay una diferencia entre las variables del recinto y "esto". "esto" en realidad está definido por el invocador de la función, mientras que las variables explícitas permanecen intactas dentro del bloque de declaración de función conocido como enclosure. Vea el ejemplo a continuación:

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

puedes probarlo aquí: http://jsfiddle.net/kSTBy/

Lo que está sucediendo en su función es "doSomeEffects ()", se llama explícitamente, esto significa contexto o el "esto" de la función es la ventana. si "doSomeEffects" era un método prototipo, por ejemplo this.doSomeEffects on say "myObject", myObject.doSomeEffects () haría que "this" fuera "myObject".

Shane
fuente
4
para los perezosos e impacientes, esta demostración registra:_this.name = myFirstObject this.name = mySecondObject
ptim
9

Para comprender esta pregunta, intente obtener el resultado del siguiente fragmento

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

El código anterior generará lo siguiente en la consola:

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

En la función externa, tanto this como self se refieren a myObject y, por lo tanto, ambos pueden hacer referencia y acceder correctamente a foo.

En la función interna, sin embargo, esto ya no se refiere a myObject. Como resultado, this.foo no está definido en la función interna, mientras que la referencia a la variable local self permanece dentro del alcance y es accesible allí. (Antes de ECMA 5, esto en la función interna se referiría al objeto de ventana global; mientras que, a partir de ECMA 5, esto en la función interna no estaría definido).

ronakshah725
fuente
1
En la función interna, se thisrefiere al objeto de ventana (en un entorno de navegador) o al objeto GLOBAL (en un entorno de node.js)
raneshu
2
¿Por qué pasa eso?
setu
@raneshu "usa estricto" y thisya no se refiere a la ventana
rofrol
3

Como explica Kyle, puede usar callo applypara especificar thisdentro de la función:

Aquí está ese concepto aplicado a su 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
fuente
0

También recibí una advertencia " Acceso de referencia potencialmente no válido a un campo de clase a través de este "

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
fuente
0

Como no se mencionó, mencionaré que usar .bind()es una solución:


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

    std_obj.displayMe();

Aquí hay un ejemplo más simple:

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'

Es cierto que el uso de una función de flecha =>parece más elegante y es fácil para el programador. Sin embargo, debe tenerse en cuenta que es probable que un alcance léxico requiera más trabajo en términos de procesamiento y memoria para configurar y mantener ese alcance léxico, en comparación con simplemente asociar una función thiscon un puntero vía .bind().

Parte del beneficio de desarrollar clases en JS fue proporcionar un método para hacer thispresente y disponible de manera más confiable, para alejarse de la programación funcional y los ámbitos léxicos y, por lo tanto, reducir la sobrecarga.

De MDN

Consideraciones de rendimiento No es aconsejable crear funciones innecesariamente dentro de otras funciones si no se necesitan cierres para una tarea en particular, ya que afectará negativamente el rendimiento del script tanto en términos de velocidad de procesamiento como de consumo de memoria.

Craig Hicks
fuente