2008-12-03 21 views
171

Utilizando los métodos de instancia como devoluciones de llamada para los controladores de eventos cambia el alcance de this de "Mi ejemplo" a "Lo que acaba de llamar la devolución de llamada". Mi código es asívar self = esto?

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

+13

Debe evitar 'self' ya que hay un objeto' window.self' y usted podría terminar con que accidentalmente si se olvida de declarar su propio 'self' var (por ejemplo, cuando se mueve alrededor de un poco de código). Esto puede ser molesto de detectar/depurar. Es mejor usar algo como '_this'. –

+5

relacionado: [¿Qué var que = esto; significa en javascript?] (http://stackoverflow.com/q/4886632/1048572) – Bergi

+3

Del [JavaScript Tutorial] (http://javascript.info/tutorial/this): "El valor de' this' es dinámico en JavaScript. Se determina cuando la función * se llama *, no cuando se declara ". – DavidRR

Respuesta

191

Esta pregunta no es específico de jQuery, pero adaptadas a JavaScript en general. El problema principal es cómo "canalizar" una variable en funciones incorporadas. 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 con this porque this es una variable que puede cambiar seudo ámbito de alcance de forma dinámica:

// 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? Asignarla a alguna variable y utilizarlo 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! 
    } 
    ... 
}; 

this no es único en este sentido: arguments es la otra variable de pseudo que debe ser tratada de la misma manera — por aliasing.

+6

Debería vincular "esto" a la llamada de función y no pasarlo ni recorrer los ámbitos de variable con él – michael

+6

@michael ¿Por qué es eso? –

1

creo que realmente depende de lo que se va a hacer dentro de su función doSomething. Si va a acceder a MyObject propiedades usando esta palabra clave, entonces debe usar eso. Pero creo que el siguiente fragmento de código también funcionará si no está haciendo cosas especiales usando las propiedades object(MyObject).

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

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

}); 
16

Sí, parece ser un estándar común. Algunos codificadores usan uno mismo, otros me usan. Se usa como referencia para el objeto "real" en lugar del evento.

Es algo que me tomó un poco de tiempo para conseguir realmente, que tiene un aspecto extraño al principio.

por lo general hago este derecho en la parte superior de mi objeto (excusa mi código de demostración - es más conceptual que otra cosa y no es una lección sobre la 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 
    } 
} 
3

yo no tengo usó jQuery, pero en una biblioteca como Prototype puede vincular funciones a un alcance específico. Así que 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.

+0

Análoga a: '$ ('# foobar'). Ready ('click', this.doSomething .call (esto)); '¿correcto? – Muers

+0

El método 'bind 'no funciona en navegadores más antiguos, por lo tanto, utilice el método' $ .proxy' en su lugar. – Guffa

6
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 que las funciones anidadas dentro de un objeto adoptan el objeto de ventana global en lugar del objeto circundante.

+0

Parece que describe lo que 'var self = this' hace, pero eso no es lo que hace la pregunta (la pregunta es si es la mejor solución al problema). – Quentin

+0

"El problema principal es cómo" canalizar "una variable en las funciones incorporadas". 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 pregunta "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

1

Solo agregue a esto que en ES6 debido a arrow functions no debería necesitar hacer esto porque capturan el valor this.

2

Una solución a esto es para prender a todos su devolución de llamada a su objeto con el método de Javascript bind.

Usted puede hacer esto con un método llamado,

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

Haciendo estos en lugar de recurrir a var self = this muestra a entender cómo la unión de this se comporta en javascript y no confía en una referencia de cierre.

Además, el operador de grasa flecha en la ES6 básicamente es lo mismo un llamado .bind(this) en una función anónima:

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

Personalmente, creo que .bind (esto) es simplemente más tipeo, cambiar la referencia a mí (o lo que sea) y luego usarlo en su lugar hace que el código sea más limpio. La flecha grasa es el futuro aunque – davidbuttar

7

Si usted está haciendo ES2015 o hacer la escritura tipo y ES5 continuación, puede utilizar las funciones de dirección en su código y no se enfrenta a ese error y esto se refiere a su alcance deseado en su instancia.

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

Como una explicación: en ES2015, las funciones de flecha capturan '' this'' de su ámbito de definición. Las definiciones de funciones normales no hacen eso. – defnull

+0

@defnull No creo que sea nada especial. Las funciones crean un alcance de bloque. así que usar la notación de flecha en lugar de una función evita que crees un ámbito, por lo tanto, esto todavía se referirá a la parte externa de esta variable. –