¿Qué subyace en este lenguaje JavaScript: var self = this?

357

Vi lo siguiente en la fuente de WebKit HTML 5 SQL Storage Notes Demo :

function Note() {
  var self = this;

  var note = document.createElement('div');
  note.className = 'note';
  note.addEventListener('mousedown', function(e) { return self.onMouseDown(e) }, false);
  note.addEventListener('click', function() { return self.onNoteClick() }, false);
  this.note = note;
  // ...
}

El autor usa self en algunos lugares (el cuerpo de la función) y esto en otros lugares (los cuerpos de las funciones definidos en la lista de argumentos de los métodos). ¿Que esta pasando? Ahora que lo he notado una vez, ¿comenzaré a verlo en todas partes?

Thomas L Holaday
fuente
44
Esta es una característica del lenguaje JS llamada "cierre léxico".
SubZero
2
Posible duplicado de: var self = this? .
DavidRR
El concepto de ESTE se explica explícitamente aquí scotch.io/@alZami/understanding-this-in-javascript
AL-zami
Ejemplo relevante en esta respuesta stackoverflow.com/a/20279485/5610569 (a la pregunta "¿Cómo acceder a la correcta thisdentro de una devolución de llamada?")
FluxLemur

Respuestas:

431

Vea este artículo en alistapart.com . (Ed: El artículo ha sido actualizado desde el enlace original)

selfse utiliza para mantener una referencia al original thisincluso cuando el contexto está cambiando. Es una técnica de uso frecuente en los controladores de eventos (especialmente en cierres).

Editar: Tenga en cuenta que selfahora se desaconseja el uso como window.selfexiste y tiene el potencial de causar errores si no tiene cuidado.

Lo que llamas variable no importa particularmente. var that = this;está bien, pero no hay nada mágico en el nombre.

Las funciones declaradas dentro de un contexto (por ejemplo, devoluciones de llamada, cierres) tendrán acceso a las variables / funciones declaradas en el mismo alcance o superior.

Por ejemplo, una devolución de llamada de evento simple:

function MyConstructor(options) {
  let that = this;

  this.someprop = options.someprop || 'defaultprop';

  document.addEventListener('click', (event) => {
    alert(that.someprop);
  });
}

new MyConstructor({
  someprop: "Hello World"
});

Jonathan Fingland
fuente
Parece que el artículo se transformó en usovar that = this;
Bob Stein
@BobStein Gracias. Actualizaré la respuesta en consecuencia.
Jonathan Fingland el
96

Creo que el nombre de la variable 'self' ya no debería usarse de esta manera, ya que los navegadores modernos proporcionan una variable global queself apunta al objeto global de una ventana normal o de un WebWorker.

Para evitar confusiones y posibles conflictos, puede escribir var thiz = thiso en su var that = thislugar.

Duan Yao
fuente
43
Usualmente uso_this
djheru
66
@djheru +1. mucho mejor que " that" (a lo que mi cerebro nunca se acostumbrará).
o_o_o--
99
Empecé a usar "yo" :)
Mopparthy Ravindranath
3
Hasta que los navegadores modernos comiencen a proporcionar una variable global _esto, eso o yo.
Beejor
10
No hay absolutamente ningún problema con el uso del nombre self, siempre y cuando lo declare como varresponsable, va a sombrear lo global. Por supuesto, si olvidaste el var, tampoco funcionaría con ningún otro nombre.
Bergi
34

Sí, lo verás en todas partes. Es a menudo that = this;.

¿Ves cómo selfse usa dentro de las funciones llamadas por eventos? Esos tendrían su propio contexto, por lo que selfse utiliza para mantener lo thisque entró Note().

La razón selfaún está disponible para las funciones, aunque solo pueden ejecutarse una vez que la Note()función ha terminado de ejecutarse, es que las funciones internas obtienen el contexto de la función externa debido al cierre .

Nosredna
fuente
12
Para mí, el punto convincente es que selfno tiene un significado especial. Personalmente, prefiero usar una var con un nombre diferente, selfya que con frecuencia me confunde, ya que espero que 'self' sea una palabra reservada. Entonces me gusta tu respuesta. Y en el ejemplo del OP, preferiría var thisNote = thiso similar.
Steve
@steve estuvo de acuerdo, aunque trato de evitar el uso de estas / auto referencias en general, ya que son muy frágiles en términos de mantenibilidad.
mattLummus
28

También debe tenerse en cuenta que hay un patrón Proxy alternativo para mantener una referencia al original thisen una devolución de llamada si no le gusta el var self = thisidioma.

Como se puede llamar a una función con un contexto dado usando function.applyo function.call, puede escribir un contenedor que devuelva una función que llame a su función con applyo callusando el contexto dado. Vea la proxyfunción de jQuery para una implementación de este patrón. Aquí hay un ejemplo de uso:

var wrappedFunc = $.proxy(this.myFunc, this);

wrappedFuncentonces se puede llamar y tendrá su versión thiscomo contexto.

Max
fuente
10

Como otros han explicado, var self = this;permite que el código en un cierre haga referencia al ámbito principal.

Sin embargo, ahora es 2018 y ES6 es ampliamente compatible con todos los principales navegadores web. El var self = this;idioma no es tan esencial como lo era antes.

Ahora es posible evitarlo var self = this;mediante el uso de funciones de flecha .

En casos donde hubiéramos usado var self = this:

function test() {
    var self = this;
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", function() {
        console.log(self.hello); // logs "world"
    });
};

Ahora podemos usar una función de flecha sin var self = this:

function test() {
    this.hello = "world";
    document.getElementById("test_btn").addEventListener("click", () => {
        console.log(this.hello); // logs "world"
    });
};

Las funciones de flecha no tienen las suyas propias thisy simplemente asumen el alcance envolvente.

Elliot B.
fuente
O ... ¡sorpresa, horror! - ¿Por qué no pasar lo relevante como argumento a su función (cierre)? ¿Por qué demonios estás haciendo referencia a un estado fuera de alcance, por qué demonios hay alguien programando así? No es que nunca ninguna razón para hacer esto. En cambio, .addEventListender("click", (x) => { console.log(x); });ha explicado el cómo y el por qué muy claramente, y estoy de acuerdo en que usar las funciones de flecha tiene más sentido, pero aún así ... esto es simplemente terrible, vago, desordenado, la programación.
Benjamin R
2
En JavaScript, hacer referencia al ámbito principal es extremadamente común y necesario. Es una parte fundamental del lenguaje. Su sugerencia de pasar en el ámbito primario como argumento en el controlador de eventos no es realmente posible. Además, en ES6, las funciones de flecha usan el alcance léxico: 'esto' se refiere a su alcance actual y no más allá, no es "hacer referencia fuera del estado del alcance" ni nada de eso.
Elliot B.
9

La variable es capturada por las funciones en línea definidas en el método. thisen la función se referirá a otro objeto. De esta forma, puede hacer que la función contenga una referencia a thisen el ámbito externo.

Mehrdad Afshari
fuente
9

Es una peculiaridad de JavaScript. Cuando una función es una propiedad de un objeto, más acertadamente llamado método, esto se refiere al objeto. En el ejemplo de un controlador de eventos, el objeto contenedor es el elemento que activó el evento. Cuando se invoca una función estándar, esto se referirá al objeto global. Cuando ha anidado funciones como en su ejemplo, esto no se relaciona en absoluto con el contexto de la función externa. Funciones internas hacen alcance acción con la función que contiene, por lo que los desarrolladores podrán utilizar variaciones de var that = thiscon el fin de preservar el este que necesitan en la función interna.

kombat
fuente
5

En realidad, self es una referencia a window ( window.self), por lo tanto, cuando dice var self = 'something'que anula una referencia de ventana a sí mismo, porque self existe en el objeto de ventana.

Es por eso que la mayoría de los desarrolladores prefieren var that = thisavar self = this;

De todas formas; var that = this;no está en línea con las buenas prácticas ... suponiendo que su código sea revisado / modificado más tarde por otros desarrolladores, debe usar los estándares de programación más comunes con respecto a la comunidad de desarrolladores

Por lo tanto, debe usar algo como var oldThis/ var oThis/ etc, para que quede claro en su alcance // .. no es tanto, pero ahorrará unos segundos y pocos ciclos cerebrales

SorinN
fuente
2
@prior Creo que tiene sentido hasta el último párrafo.
miphe
0

Como se mencionó varias veces anteriormente, 'self' simplemente se usa para mantener una referencia a 'this' antes de ingresar a la función. Una vez en la función 'esto' se refiere a otra cosa.

Cyprien
fuente
@JohnPaul ... esto hace dar una respuesta. Puede que no sea la respuesta correcta, pero ¿cómo puedes decir que "está acostumbrado a ..." no es una respuesta a "por qué hizo esto"?
GreenAsJade
-1
function Person(firstname, lastname) {
  this.firstname = firstname;

  this.lastname = lastname;
  this.getfullname = function () {
    return `${this.firstname}   ${this.lastname}`;
  };

  let that = this;
  this.sayHi = function() {
    console.log(`i am this , ${this.firstname}`);
    console.log(`i am that , ${that.firstname}`);
  };
}

let thisss = new Person('thatbetty', 'thatzhao');

let thatt = {firstname: 'thisbetty', lastname: 'thiszhao'};

thisss.sayHi.call (thatt);

ht zhao
fuente
1
Debería agregar alguna explicación con código que lo que hizo especial.
Farhana