2010-04-11 13 views
6

Estoy intentando crear un objeto UserDon e intentando generar los métodos get y set programáticamente (basado en el libro Pro Javascript de John Resig página 37) y estoy probando esto en Firefox 3.5Creando el método get/set dinámicamente en javascript

El problema es: en la función UserDon, "esto" se refiere al objeto ventana en lugar del objeto UserDon.

Así que después de llamar a var userdon = new UserDon (...) Obtuve los métodos setname y getname creados en el objeto window (también setage y getage).

¿Cómo puedo solucionar esto?

function UserDon(properties) { 
    for(var i in properties) { 
    (function(){ 
     this[ "get" + i ] = function() { 
     return properties[i]; 
     }; 

     this[ "set" + i ] = function(val) { 
     properties[i] = val; 
     }; 
     })(); 
    } 
} 

var userdon = new UserDon({ 
    name: "Bob", 
    age: 44 
}); 
+1

Buena pregunta si se trata de un ejercicio.De lo contrario, si no está haciendo ninguna operación adicional en getters/setters, es de esperar que simplemente use las propiedades públicas y renuncie a la sobrecarga del método. :) – deceze

Respuesta

7

El valor this está utilizando pertenece a la expresión de la función de auto-invocando que tiene dentro del bucle, y cuando se invoca una función de esta manera, se this siempre se refieren al objeto global.

Editar: echaba de menos el hecho de que la expresión de la función es tratando para hacer la captura de variables para manejar la creación de captador/definidor dentro del bucle, pero el bucle variable de i, necesita ser pasado como argumento con el fin para hacerlo y ya que la expresión de la función es no, el contexto (el exterior this) debe preservarse:

function UserDon(properties) { 
    var instance = this; // <-- store reference to instance 

    for(var i in properties) { 
    (function (i) { // <-- capture looping variable 
     instance[ "get" + i ] = function() { 
     return properties[i]; 
     }; 

     instance[ "set" + i ] = function(val) { 
     properties[i] = val; 
     }; 
    })(i); // <-- pass the variable 
    } 
} 

var userdon = new UserDon({ 
    name: "Bob", 
    age: 44 
}); 

userdon.getname(); // "Bob" 
userdon.getage(); // 44 

también puede utilizar el método call para invocar la expresión de función, preservando el contexto (el valor de this) y presenta cing la variable de bucle para el nuevo alcance en un solo paso:

function UserDon(properties) { 
    for(var i in properties) { 
    (function (i) { // <-- looping variable introduced 
     this[ "get" + i ] = function() { 
     return properties[i]; 
     }; 

     this[ "set" + i ] = function(val) { 
     properties[i] = val; 
     }; 
    }).call(this, i); // <-- preserve context and capture variable 
    } 
} 

I también recomendaría el uso de un if (properties.hasOwnProperty(i)) { ... } dentro del bucle for...in para evitar iterar sobre propiedades extendidas de usuario heredadas de Object.prototype.

+1

Sin la función de invocación automática, getname() y getage() devolverán 44, porque el cierre hace referencia al último valor de la matriz, que es la edad, consulte la página 29 del libro. – portoalet

+0

@portoalet: Tiene razón, olvidé ese punto, pero eso no es suficiente, la variable 'i' debe pasarse como argumento para esa función, también debe conservarse el valor' this', vea mi edición. – CMS

+0

Gracias, ahora funciona perfectamente. – portoalet

0

Podría ser una mejor idea obtener una función genérica con 2 parámetros: el nombre de la propiedad y el valor (o solo el nombre del comprador). Y esta función verificaría la presencia de una función especial para esta propiedad y si no la hay, simplemente cambiaría el valor de la propiedad (o devolvería su valor para getter).

0

Aquí es cómo iba a codificarlo: -

function UserDon(properties) { 
    var self = this; 
    for(var i in properties) { 
    (function(prop){ 
     self[ "get" + prop ] = function() { 
     return properties[prop]; 
     }; 

     self[ "set" + prop ] = function(val) { 
     properties[prop] = val; 
     }; 
     })(i); 
    } 
} 
1

También puede utilizar el menos conocidos
__defineGetter__("varName", function(){});
y __defineSetter__("varName", function(val){});

A pesar de que no son estándar [como x-html-replace content-type] son ​​compatibles con la mayoría de los navegadores que no son, por ejemplo, [cromo, firefox]

La sintaxis sería:

benjamin = new object(); 
benjamin.__defineGetter__("age", function(){ 
    return 21; 
}); 

O puede acercarse a esto con prototipos

benjamin = { 
    get age() 
    { 
    return 21; 
    } 
} 
+0

nunca vi esto antes. ¿Estás usando esto tú mismo? – portoalet

+1

Solo lo uso para depurar donde se está obteniendo/configurando una variable. Usted defineSetter ("variableName", outputStackTrace); y funciona con estilos CSS también. Por ejemplo, dices 'object.style .__ defineGetter __ (" left ", outputStackTraceFunction);' Por alguna razón, las personas no conocen esta función. Probablemente porque es no estándar? – Warty

Cuestiones relacionadas