2012-03-04 14 views
8

Dentro de John Resig "Pro técnicas javascript" que describe una forma de generar métodos de objetos dinámicos con el código de abajo:"esto" dentro de una función anónima? libro

// Create a new user object that accepts an object of properties 
function User(properties) { 
    // Iterate through the properties of the object, and make sure 
    // that it's properly scoped (as discussed previously) 
    for (var i in properties) { 
     (function() { 
      // Create a new getter for the property 
      this["get" + i] = function() { 
       return properties[i]; 
      }; 
      // Create a new setter for the property 
      this["set" + i] = function(val) { 
       properties[i] = val; 
      }; 
     })(); 
    } 
} 

El problema es cuando intento crear instancias del objeto anterior, los métodos dinámicos están siendo unidos a el objeto ventana en lugar del objeto instanciado. Parece que "esto" se refiere a la ventana.

// Create a new user object instance and pass in an object of 
// properties to seed it with 
var user = new User({ 
name: "Bob", 
age: 44 
}); 

alert(user.getname()); 

Al ejecutar el código anterior se arroja este error "user.getname no es una función".

Cuál es la forma correcta de generar las funciones dinámicas para cada instancia de objeto?

+7

Estoy seguro de que John Resig usó la sangría adecuada. –

+1

Esto no parece correcto. Dentro de la función anónima, 'this' es' window'. –

+0

La respuesta es * no * sangría, pero ** es sangría **. Discutir. –

Respuesta

11

¿Este código es del libro? Tengo el libro, pero no lo he leído.

se trata de un error en el libro. Compruebe la errata: http://www.apress.com/9781590597279

Dentro de la función anónima, this es el window global.

Se podría hacer que funcione mediante la adición de .call(this, i) después de ella.

function User(properties) { 
    // Iterate through the properties of the object, and make sure 
    // that it's properly scoped (as discussed previously) 
    for (var i in properties) { 
     (function(i) { 
      // Create a new getter for the property 
      this["get" + i] = function() { 
       return properties[i]; 
      }; 
      // Create a new setter for the property 
      this["set" + i] = function(val) { 
       properties[i] = val; 
      }; 
     }).call(this, i); 
    } 
} 
+2

* Tengo el libro, pero aún no lo he leído. * Entonces, ¿qué ** he estado haciendo ** con él? ¿Librería? Fuente de fibra leñosa? ¿Papel pintado? –

+2

+1 para también solucionar el problema con 'i' capturado. – ruakh

+0

@JaredFarrish: Se usa para mantener los libros en mi estante simétricos. No, pero en serio, solo necesito algo de tiempo para leerlo :-P –

3

El this en la función interna de autoejecución no es el mismo que el de la función User externa. Como habrás notado, se refiere al global window.

El problema se resuelve si se modifica ligeramente el código mediante la adición de una variable que se refiere al exterior this.

function User(properties) { 
    var self = this; 
    for (var i in properties) { 
    (function() { 
     self["get" + i] = function() { /* ... */ }; 
     self["set" + i] = function() { /* ... */ }; 
    })(); 
    } 
} 

Dicho esto, no estoy seguro de por qué la función anónima autoejecutable es incluso necesario aquí, para que tenga la opción más simple de poco dejándolo por completo, así:

function User(properties) { 
    for (var i in properties) { 
     this["get" + i] = function() { /* ... */ }; 
     this["set" + i] = function() { /* ... */ }; 
    } 
} 
1

Aquí está cómo hacerlo. Debe guardar el contexto en otra variable. La otra opción es no hacer esta función interna que está haciendo en el ciclo for.

// Create a new user object that accepts an object of properties 
function User(properties) { 
    // Iterate through the properties of the object, and make sure 
    // that it's properly scoped (as discussed previously) 
    var that = this; 
    for (var i in properties) { (function(){ 
     // Create a new getter for the property 
     that[ "get" + i ] = function() { 
      return properties[i]; 
     }; 
     // Create a new setter for the property 
     that[ "set" + i ] = function(val) { 
      properties[i] = val; 
     }; 
    })(); } 
} 

Opción 2:

// Create a new user object that accepts an object of properties 
function User(properties) { 
    // Iterate through the properties of the object, and make sure 
    // that it's properly scoped (as discussed previously) 
    for (var i in properties) { 
     // Create a new getter for the property 
     this[ "get" + i ] = function() { 
      return properties[i]; 
     }; 
     // Create a new setter for the property 
     this[ "set" + i ] = function(val) { 
      properties[i] = val; 
     }; 
    } 
} 
+0

Ambas opciones no funcionan ya que usan la versión capturada de 'i' que cambia a medida que se ejecuta el ciclo y deja 'i' como el último nombre de propiedad. Parte del ejemplo de John fue cómo evitar este problema. Sin embargo, la opción 1 puede arreglarse fácilmente al pasar 'i' como parámetro. Para reparar la Opción 2, la convertiría en una versión fija de la Opción 1. – chuckj

1

Siempre se puede obligar a otro this para cualquier llamada de función, usando el método apply.

(function() { 
    // Create a new getter for the property 
    this["get" + i] = function() { 
     return properties[i]; 
    }; 
    // Create a new setter for the property 
    this["set" + i] = function(val) { 
     properties[i] = val; 
    }; 
}).apply(this); 
Cuestiones relacionadas