var self = esto?


182

El uso de métodos de instancia como devoluciones de llamada para los controladores de eventos cambia el alcance de thisde "Mi ejemplo" a "Lo que acaba de llamar la devolución de llamada" . Entonces mi código se ve así

function MyObject() {
  this.doSomething = function() {
    ...
  }

  var self = this
  $('#foobar').bind('click', function(){
    self.doSomethng()
    // this.doSomething() would not work here
  })
}

Funciona, pero ¿es esa la mejor manera de hacerlo? Me parece extraño


15
Debe evitarlo selfya que hay un window.selfobjeto y podría terminar usándolo accidentalmente si olvida declarar su propia selfvar (por ejemplo, cuando mueve algún código). Esto puede ser molesto para detectar / depurar. Es mejor usar algo como _this.
Alastair Maw


3
Del tutorial de JavaScript : "El valor de thises dinámico en JavaScript. Se determina cuando se llama a la función , no cuando se declara".
DavidRR


La cosa es, en el ámbito global self === this. Por lo tanto, selfen contextos locales tiene sentido y sigue el patrón.
programador

Respuestas:


210

Esta pregunta no es específica de jQuery, sino específica de JavaScript en general. El problema central es cómo "canalizar" una variable en funciones integradas. Este es el ejemplo:

var abc = 1; // we want to use this variable in embedded functions

function xyz(){
  console.log(abc); // it is available here!
  function qwe(){
    console.log(abc); // it is available here too!
  }
  ...
};

Esta técnica se basa en el uso de un cierre. Pero no funciona thisporque thises una pseudo variable que puede cambiar de un alcance a otro dinámicamente:

// we want to use "this" variable in embedded functions

function xyz(){
  // "this" is different here!
  console.log(this); // not what we wanted!
  function qwe(){
    // "this" is different here too!
    console.log(this); // not what we wanted!
  }
  ...
};

¿Qué podemos hacer? Asignarlo a alguna variable y usarlo a través del alias:

var abc = this; // we want to use this variable in embedded functions

function xyz(){
  // "this" is different here! --- but we don't care!
  console.log(abc); // now it is the right object!
  function qwe(){
    // "this" is different here too! --- but we don't care!
    console.log(abc); // it is the right object here too!
  }
  ...
};

thisno es único a este respecto: argumentses la otra pseudo variable que debe tratarse de la misma manera, mediante alias.


77
Realmente deberías vincular "esto" a la llamada a la función y no pasarlo ni atravesar ámbitos variables con él
Michael

10
@michael ¿Por qué es eso?
Chris Anderson

@michael podría estar trabajando dentro de una tubería, un mapa, suscribirse ... en una función y no llamando a funciones, en este caso "esto" está cambiando, por lo que el alcance variable es la solución
Skander Jenhani

@SkanderJenhani ese comentario tiene 8 años. Mi respuesta sería muy diferente hoy
michael

18

Sí, esto parece ser un estándar común. Algunos codificadores se usan a sí mismos, otros me usan a mí. Se utiliza como referencia de regreso al objeto "real" en lugar del evento.

Es algo que me tomó un poco de tiempo entender, al principio parece extraño.

Por lo general, hago esto justo en la parte superior de mi objeto (disculpe mi código de demostración, es más conceptual que cualquier otra cosa y no es una lección sobre una excelente técnica de codificación):

function MyObject(){
  var me = this;

  //Events
  Click = onClick; //Allows user to override onClick event with their own

  //Event Handlers
  onClick = function(args){
    me.MyProperty = args; //Reference me, referencing this refers to onClick
    ...
    //Do other stuff
  }
}

12
var functionX = function() {
  var self = this;
  var functionY = function(y) {
    // If we call "this" in here, we get a reference to functionY,
    // but if we call "self" (defined earlier), we get a reference to function X.
  }
}

editar: a pesar de, las funciones anidadas dentro de un objeto toman el objeto de la ventana global en lugar del objeto circundante.


Parece que está describiendo lo que var self = thishace, pero eso no es lo que la pregunta está haciendo (la pregunta es si esa es la mejor solución al problema).
Quentin

3
"El problema central es cómo" canalizar "una variable en funciones integradas". Estoy de acuerdo con esta afirmación en la respuesta aceptada. Además de eso, no hay nada de malo en "esa" práctica. Es bastante común. Entonces, como respuesta a la parte "el código me parece extraño", elegí explicar cómo funciona. Creo que la razón por la que el "código me parece extraño" proviene de la confusión de cómo funciona.
serkan

12

Si está haciendo ES2015 o está haciendo script de tipo y ES5, entonces puede usar funciones de flecha en su código y no enfrenta ese error y esto se refiere al alcance deseado en su instancia.

this.name = 'test'
myObject.doSomething(data => {
  console.log(this.name)  // this should print out 'test'
});

55
Como explicación: en ES2015 las funciones de flecha capturan thisdesde su alcance definitorio. Las definiciones de funciones normales no hacen eso.
desactivado el

@defnull No creo que sea nada especial. Las funciones crean un alcance de bloque. por lo tanto, el uso de la notación de flecha en lugar de una función evita que cree un ámbito, por lo que esto todavía se referirá a la variable externa.
Nimeshka Srimal

4

Una solución a esto es vincular toda su devolución de llamada a su objeto con el bindmétodo de JavaScript .

Puedes hacer esto con un método con nombre,

function MyNamedMethod() {
  // You can now call methods on "this" here 
}

doCallBack(MyNamedMethod.bind(this)); 

O con una devolución de llamada anónima

doCallBack(function () {
  // You can now call methods on "this" here
}.bind(this));

Hacer esto en lugar de recurrir a var self = thismuestra que comprende cómo se thiscomporta el enlace de javascript y no se basa en una referencia de cierre.

Además, el operador de flecha gruesa en ES6 básicamente es lo mismo que invocar .bind(this)una función anónima:

doCallback( () => {
  // You can reference "this" here now
});

Personalmente, creo que .bind (this) es solo más tipeo, me cambia la referencia this (o lo que sea) y luego simplemente usarlo en su lugar crea un código más limpio. La flecha gorda es el futuro aunque.
davidbuttar

3

No he usado jQuery, pero en una biblioteca como Prototype puede vincular funciones a un ámbito específico. Entonces, con eso en mente, su código se vería así:

 $('#foobar').ready('click', this.doSomething.bind(this));

El método de enlace devuelve una nueva función que llama al método original con el alcance que ha especificado.


Análogo a: $('#foobar').ready('click', this.doSomething.call(this));¿verdad?
Mueres el

El bindmétodo no funciona en navegadores antiguos, por lo tanto, utilice el $.proxymétodo.
Guffa


1

Creo que en realidad depende de qué vas a hacer dentro de tu doSomethingfunción. Si va a acceder a las MyObjectpropiedades usando esta palabra clave, entonces debe usarla. Pero creo que el siguiente fragmento de código también funcionará si no está haciendo cosas especiales usando object(MyObject)propiedades.

function doSomething(){
  .........
}

$("#foobar").ready('click', function(){

});
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.