2009-02-12 14 views
8

Pregunto esto desde el punto de vista del diseño del lenguaje. Así que estoy tratando de averiguar¿Cuál es el fundamento del comportamiento de la palabra clave "this" en JavaScript?

  1. ¿Cuál es la razón fundamental para el comportamiento de this?
  2. ¿En qué medida el comportamiento de this fue un error o podría mejorarse?

Para aclarar por qué estoy inquieto por this, considere este ejemplo:

var a = {}; 
a.f = function(){ return this; } 
var f = a.f; 
// f() != a.f() 

Nota cuán fácilmente el objeto al que pertenecen f() se pierde: separado de a, this se convierte en el mundial objeto (window para navegadores).

Consideremos ahora:

var newA = function(){ 
    var self = {}; 
    self.f = function(){ return self; } 
    return self; 
} 

var a = newA(); 
var f = a.f; 
// f() == a.f() ! 

Sin utilizar this en absoluto, estamos en condiciones de establecer y mantener el contexto del objeto, independientemente de dónde o cómo se utiliza el método. No puedo evitar pensar que, con el poder que proporcionan cierres, this se vuelve superflua, y tal vez incluso un poco peligroso ...

no estoy en alguna venganza contra this, o en busca de iniciar una discusión; Simplemente estoy tratando de entenderlo mejor. Aprecio que 'esto' puede ser útil, pero reconozco que it can be confusing también ... Ciertamente confunde a los principiantes, y tal vez a los expertos también en casos suficientemente oscuros.

Y, sin embargo, sigue siendo una parte muy utilizada y aparentemente respetada del lenguaje, en un momento en que otros aspectos centrales del lenguaje parecen un juego justo para rechazar (es decir, Crockford y with o new). ¿Qué me estoy perdiendo entonces, eso hace que this sea indispensable?

+0

¿Qué le da la impresión de que "esto" es un error? –

+0

@Gamecat: nunca has intentado diseñar un lenguaje similar al de JavaScript, ¿o sí? – artificialidiot

+0

Sigo pensando que esto es discutidor. El lenguaje implica un problema, cf "Qué sorprendente es 'esto' (si es sorprendente)" – annakata

Respuesta

11

usted parece estar esperando que este se comporte como lo hace en ciertos lenguajes orientados a objetos, donde siempre se refiere al objeto un método pertenece.

Pero en JavaScript, una función se puede conectar a varios objetos, o no tener ningún objeto. En su ejemplo, ha escrito una función destinada a ser utilizada en el contexto de un objeto específico ... Pero nada me impide tomar esa función y vincularla a cualquier otro objeto. Esa es solo la naturaleza del lenguaje: las funciones son de primera clase, la membresía de objetos es opcional.

Por lo tanto, este se refiere al contexto en el que se llama a una función. En este momento, eso es un objeto arbitrario (especificado a través de ., .apply o .call()) o el objeto global. En versiones futuras del lenguaje, se referirá al contexto en el que se definió la función: el objeto global para funciones globales, el exterior this para funciones internas; puede ver esto como una corrección de un defecto de diseño, ya que en la práctica ser capaz de referirse al objeto global utilizando esto no fue particularmente útil.

3
  • No fue
  • montón de cosas OO
  • Es buena la forma en que es
+1

Ok. ¿Te importaría dar ejemplos de lo que OO no es posible? –

+0

Esta es una respuesta muy poco útil, ya que el autor declara la opinión como un hecho sin hacer una copia de seguridad con el razonamiento o los ejemplos. -1 – rix0rrr

+0

mike g: hacer funciones públicas rix0rr: es bastante difícil respaldar los hechos ya que el otro lado no proporciona ningún – svinto

1
  • Debería llamarse 'yo' en lugar
  • Todo lo que se refiere a la corriente estado de los objetos.
  • Escogiendo 'self' como el nombre de 'esto', y pasándolo explícitamente (como primer argumento) a todos los métodos. De esta forma, puede distinguir fácilmente el método de instancia del método estático o de la función.

Lo sentimos, pero me gusta mucho de Python ;-)

+0

Creo que debería llamarse Yo;) –

+0

Whoa, alguien no tiene sentido del humor? ¿Voto a favor de 'esto'? – Abgan

+0

Olvidó mencionar que desea que se pase 'self' como primer argumento a un método en lugar de tener acceso implícito a él. –

3

Creo sin consolidar "este" es un error. De lo contrario, es bastante útil. Sin consolidar "esto" abre la posibilidad de malinterpretar el contexto más visible en el manejo de eventos de navegadores. Además, las bibliotecas Javascript tienen diferentes opiniones sobre a qué se debe referir "esto" en el manejo de eventos y muchas construcciones de devolución de llamada (como mapa, filtro).

Eliminar unbound "esto" probablemente no haría las cosas más difíciles.

Editar: Supongo que un ejemplo de sintaxis alternativo hará que mi postura sea más clara.

function Foo() 
{ 
    //both this refer to the Foo instance 
    this.blah=this.function(){this.bar;}; 

    //second this refers to baz 
    this.blah=baz.function(){this.bar;}; 

    //second this refers to anonymous function itself 
    this.blah=function(){this.bar;}; 
} 
8

No creo que hacer "esto" desatado fue un error. A veces puede ser confuso al principio, pero hay buenas razones para que sea así. El primero que me viene a la mente es que, dado que JavaScript no es un lenguaje basado en clases, las funciones no están asociadas a ninguna clase específica, por lo que no hay una forma consistente de vincular automáticamente "esto" a la instancia de objeto correcta. Por ejemplo,

function Person(first, last, age) { 
    this.firstName = first; 
    this.lastName = last; 
    this.age = age; 
} 

Person.prototype.getFullName = function() { 
    return this.firstName + " " + this.lastName; 
}; 

"este" debe referirse a un objeto de persona, pero la función asignada a Person.prototype.getName no tiene ningún modo de saber cómo va a ser utilizado, por lo que "este" debe estar vinculado a cualquier objeto al que se llame.

Donde esto causa un problema, es cuando tiene funciones anidadas.

// This is a really contrived example, but I can't think of anything better 
Person.prototype.getInfo = function() { 
    // get name as "Last, First" 
    function getNameLastFirst() { 
     // oops. "this" is the global object, *not* the Person 
     return this.lastName + ", " + this.firstName; 
    } 

    // expect something like "Crumley, Matthew: Age 25", 
    // but you get "undefined, undefined: Age 25" 
    return getNameLastFirst() + ": Age " + this.age; 
}; 

La sintaxis artificialidiot sugirió sería conveniente, pero es bastante fácil de unir "esto" a un objeto específico utilizando aplicar:

function bind(func, obj) { 
    return function() { 
     return func.apply(obj, arguments); 
    }; 
} 

Person.prototype.getInfo = function() { 
    // get name as "Last, First" 
    var getNameLastFirst = bind(function() { 
     return this.lastName + ", " + this.firstName; 
    }, this); 

    return getNameLastFirst() + ": Age " + this.age; 
}; 

o el método más "tradicional" por medio de cierres:

Person.prototype.getInfo = function() { 
    var self = this; 

    // get name as "Last, First" 
    function getNameLastFirst() { 
     return self.lastName + ", " + self.firstName; 
    } 

    return getNameLastFirst() + ": Age " + this.age; 
}; 
+0

Matthew Crumley, (No sé por qué el signo "@" no funciona, pero espero que vea esta publicación). Estoy tratando de seguir tu código en tu respuesta, pero tal vez me falta algo aquí? Dime, ¿dónde defines la función 'getNameFirstLast'? ¿Es esto un error tipográfico y por qué sus valores de retorno no están definidos? Gracias. – Chris22

+0

@ Chris22 Vaya, eso fue un error tipográfico. debería ser 'getNameLastFirst()'. La razón por la que devuelve 'undefined' en el primer ejemplo es porque se llama sin un contexto, por lo que' this' está vinculado al alcance global de forma predeterminada. Entonces (suponiendo que no haya variables globales llamadas 'lastName' o' firstName') ambas propiedades no estarán definidas en lugar de obtener los valores del objeto 'Person' como es de esperar. –

+0

+1 Matthew Crumley Ok, gracias.Entiendo el alcance de 'esto'. El error tipográfico solo distraía :-) – Chris22

1

I think la palabra clave 'this' no vinculada es necesaria porque JavaScript es un lenguaje basado en prototipos. Alguien mejor informado probablemente pueda completar los detalles aquí.

El hecho de que es, sin embargo es muy útil. Especialmente si quieres pasar el método de un objeto a una función de orden superior, las cosas empiezan a ponerse (ejemplos siguientes con un poco de ayuda de MooTools) feos:

myArray.each(myObject.foo); 

no va a funcionar, porque el 'esto' en myObject.foo se referirá a myArray en lugar de myObject. En cambio:

myArray.each(myObject.foo.bind(myObject)) 

Lo que me parece muy feo. Es por eso que generalmente no programo de forma orientada a objetos en JavaScript, pero en su lugar dependo mucho de los cierres.

1

Considérese el idioma a.f() como una abreviatura para:

a.f.call(a); 

Es por definición es una llamada a la función f, usando alcance a.

var f = a.f; 
f(); // f.call(this); 
a.f(); // f.call(a); 

Si this y a no son el mismo objeto, y f()a.f() usarán diferentes ámbitos y por lo tanto puede comportarse de manera diferente.Considere la diferencia entre métodos estáticos y de clase en otros idiomas:

class Foo { 
public: 
    static void a(Foo *scope) { 
     // do something with given scope 
    }; 

    void b() { 
     a(this); // do something with the scope of this object 
    }; 
}; 

Foo foo; 
Foo bar; 

foo.a(&bar) != foo.b(); // just like f() != a.f() 
foo.a(&foo) == foo.b(); // just like f.call(a) == a.f() 
Cuestiones relacionadas