javascript - react - Getter/Setter en constructor
que es getter y setter (6)
@Alex lo veo como más opción y más poder, la programación es arte, @Nat comparte sus hallazgos con nosotros, y por eso le agradezco. Tal vez alguien quiera hacerlo de esa manera.
Estoy seguro de que la versión del configurador es la misma pero solo cambiando g a s.
yo G:
function Constructor(input){
this.input = input;
}
Object.__defineGetter__.call(Constructor.prototype, "value", function(){
return this.input * 2;
});
Object.__defineSetter__.call(Constructor.prototype, "bar", function(foo){
return this.input *= foo;
});
var test = new Constructor(5);
console.log(test.value); // 10
test.bar = 5;
console.log(test.input); //25
Dicho esto, esta característica está en desuso, se recomienda no utilizarla en la codificación de producción.
Recientemente leí sobre el hecho de que existe la posibilidad de definir captadores / configuradores en JavaScript. Parece extremadamente útil: el definidor es un tipo de "ayudante" que puede analizar el valor que se debe establecer primero, antes de configurarlo realmente.
Por ejemplo, actualmente tengo este código:
var obj = function(value) {
var test = !!value; // ''test'' has to be a boolean
return {
get test() { return test },
set test(value) { test = !!value }
};
};
var instance = new obj(true);
Este código siempre convierte value
a un booleano. Entonces, si codifica instance.test = 0
, entonces instance.test === false
.
Sin embargo, para que esto funcione, realmente debe devolver un objeto , lo que significa que la nueva instancia no es de tipo obj
sino que simplemente es un objeto simple. Esto significa que cambiar el prototipo de obj
no tiene efecto en las instancias. Por ejemplo, esto no funciona - instance.func
no está definido:
obj.prototype.func = function() { console.log(this.value); };
Porque la instance
no es de tipo obj
. Para que las funciones de prototipo funcionen, supongo que no debería devolver un objeto simple, sino más bien no devolver nada para que la instance
sea de tipo obj
, como funciona un constructor normal.
¿El problema entonces es cómo implementar getters / setters? Solo puedo encontrar artículos que describan cómo agregarlos a un objeto, no como parte del constructor de un tipo personalizado.
Entonces, ¿cómo implemento los getters / setters en el constructor para poder usar tanto getters / setters como extender el prototipo?
Actualización para ES6: eche un vistazo a la sección 19.3.1 del libro de Alex Rauschmayer Explorando ES6 http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-data que muestra cómo usar WeakMaps con los que buscan y configuran para mantener los datos privados. Combinar con la sección 16.2.2.3 http://exploringjs.com/es6/ch_classes.html#leanpub-auto-getters-and-setters resultaría en algo como
# module test_WeakMap_getter.js
var _MyClassProp = new WeakMap();
class MyClass {
get prop() {
return _MyClassProp.get( this );
}
set prop(value) {
_MyClassProp.set( this, value );
}
}
var mc = new MyClass();
mc.prop = 5 ;
console.log( ''My value is'', mc.prop );
$ node --use_strict test_WeakMap_getter.js
My value is 5
No puedes hacer eso.
Sin embargo, puede establecer el setter / getters para las propiedades de los objetos. Aunque te aconsejo que uses ES5 Object.defineProperties
. Por supuesto, esto solo funciona en los navegadores modernos.
var obj = function() {
...
Object.defineProperties(this, {
"test": {
"get": function() { ... },
"set": function() { ... }
}
});
}
obj.prototype.func = function() { ... }
var o = new obj;
o.test;
o.func();
Por lo general, usted quiere métodos de clase . La respuesta de @Raynos el 7 de mayo de 2011 hace el trabajo, pero define un método de instancia , no un método de clase.
A continuación se ilustra una definición de clase con un getter y setter que forma parte de la clase. Esta definición se parece mucho a la respuesta de @Raynos, pero con dos diferencias en el código: (1) La acción "defineProperties ()" se ha movido fuera del constructor. (2) El argumento de "defineProperties ()" se cambió del objeto de instancia "this", al objeto prototipo del constructor.
function TheConstructor(side) {
this.side = side;
}
Object.defineProperties(TheConstructor.prototype, {
area: {
get: function() { return this.side * this.side; }
,set: function(val) { this.side = Math.sqrt(val); }
}
});
// Test code:
var anInstance = new TheConstructor(2);
console.log("initial Area:"+anInstance.area);
anInstance.area = 9;
console.log("modified Area:"+anInstance.area);
Lo que produce estos resultados:
initial Area:4
modified Area:9
Aunque generalmente la distinción entre definición de clase y instancia es solo una cuestión de estilo, el buen estilo tiene un propósito, y hay un caso en el que la distinción importa: el captador memorizado . El propósito de un captador memoized se describe aquí: Smart / auto-overwriting / perezoso getters
Defina el captador en el nivel de clase cuando el valor memorizado corresponda a toda la clase. Por ejemplo, un archivo de configuración debe leerse solo una vez; Los valores resultantes deben aplicarse durante la duración del programa. El siguiente código de ejemplo define un captador memorizado en el nivel de clase.
function configureMe() {
return 42;
}
Object.defineProperties(TheConstructor.prototype, {
memoizedConfigParam: {
get: function() {
delete TheConstructor.prototype.memoizedConfigParam;
return TheConstructor.prototype.memoizedConfigParam = configureMe();
}
,configurable: true
}
});
// Test code:
console.log("memoizedConfigParam:"+anInstance.memoizedConfigParam);
Produce:
memoizedConfigParam:42
Como se puede ver en el ejemplo, los captadores memorizados tienen la característica de que la función de captador se elimina a sí misma, luego se reemplaza a sí misma con un valor simple que (presumiblemente) nunca cambiará. Tenga en cuenta que ''configurable'' debe configurarse como ''verdadero''.
Defina el captador en el nivel de instancia cuando el valor memorizado depende del contenido de la instancia. La definición se mueve dentro del constructor, y el objeto de atención es "esto".
function TheConstructorI(side) {
this.side = side;
Object.defineProperties(this, {
memoizedCalculation: {
get: function() {
delete this.memoizedCalculation;
return this.memoizedCalculation = this.expensiveOperation();
}
,configurable: true
}
});
}
TheConstructorI.prototype.expensiveOperation = function() {
return this.side * this.side * this.side;
}
//Test code:
var instance2 = new TheConstructorI(2);
var instance3 = new TheConstructorI(3);
console.log("memoizedCalculation 2:"+instance2.memoizedCalculation);
console.log("memoizedCalculation 3:"+instance3.memoizedCalculation);
Produce:
memoizedCalculation 2:8
memoizedCalculation 3:27
Si desea garantizar (en lugar de suponer) que el valor memorizado nunca se modificará, el atributo de "escritura" debe cambiarse. Eso hace que el código sea un poco más complicado.
function TheConstructorJ(side) {
this.side = side;
Object.defineProperties(this, {
memoizedCalculation: {
get: function() {
delete this.memoizedCalculation;
Object.defineProperty( this, ''memoizedCalculation''
,{ value : this.expensiveOperation()
,writable : false
});
return this.memoizedCalculation;
}
,configurable: true
}
});
}
TheConstructorJ.prototype.expensiveOperation = function() {
return this.side * this.side * this.side;
}
//Test code:
var instanceJ = new TheConstructorJ(2);
console.log("memoizedCalculation:"+instanceJ.memoizedCalculation);
instanceJ.memoizedCalculation = 42; // results in error
Produce:
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property ''memoizedCalculation'' of object ''#<TheConstructorJ>''
La pregunta original del OP, desde el 7 de marzo de 2011, presentó la sintaxis básica de captador y definidor, señaló que funcionó en un objeto pero no en ''esto'' y preguntó cómo definir captadores y definidores dentro de un constructor. Además de todos los ejemplos anteriores, también hay una forma de hacerlo "a bajo costo": cree un nuevo objeto dentro del constructor, como lo hizo el OP, pero luego asigne el objeto para que sea un miembro dentro de "esto". Entonces, el código original se vería así:
var MyClass = function(value) {
var test = !!value; // ''test'' has to be a boolean
this.data = {
get test() { return test },
set test(value) { test = !!value }
};
};
var instance = new MyClass(true);
// But now ''data'' is part of the access path
instance.data.test = 0;
console.log(instance.data.test);
Produce:
false
Lo creas o no, en realidad me he encontrado con situaciones en las que este "disparo barato" es la mejor solución. Específicamente, utilicé esta técnica cuando tenía registros de varias tablas encapsuladas dentro de una sola clase y quería presentar una vista unificada como si fueran un solo registro llamado "datos".
Que te diviertas.
IAM_AL_X
Sé que esto puede ser extremadamente tarde, pero descubrí una manera diferente de lograr lo que quieres y por el bien de la gente, como yo, buscando en Google una respuesta a esta pregunta.
function Constructor(input){
this.input = input;
}
Object.__defineGetter__.call(Constructor.prototype, "value", function(){
return this.input * 2;
});
var test = new Constructor(5);
alert(test.value) // 10
He probado esto en Chrome, Safari, Mobile Safari, Firefox y todos funcionan (últimas versiones, por supuesto)
function Obj(value){
this.value = !!value;
}
Obj.prototype = {
get test () {
return this.value;``
},
set test (value) {
this.value = !!this.value;
}
};
var obj = new Obj(true);