我最近读到了这样一个事实,即有可能在JavaScript中定义getter / setter。它似乎非常有用 - setter是一种'帮助器',可以在实际设置之前解析要设置的值。
例如,我目前有这段代码:
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);
此代码始终将value
转换为布尔值。因此,如果您编码instance.test = 0
,则instance.test === false
。
但是,要实现此功能,您必须实际返回对象,这意味着新实例不是obj
类型,而只是一个普通对象。这意味着更改obj
的原型对实例没有影响。例如,这确实不工作 - instance.func
未定义:
obj.prototype.func = function() { console.log(this.value); };
因为instance
不是obj
类型。为了让原型函数工作,我想我不应该返回一个普通的对象,而是不返回任何东西,以便instance
只是类型obj
,就像常规构造函数一样。
问题是如何实现getter / setter?我只能找到描述如何将这些添加到对象的文章,而不是作为自定义类型的构造函数的一部分。
那么我如何在构造函数中实现getter / setter,以便能够使用getter / setter并扩展原型?
答案 0 :(得分:44)
你做不到。
您可以为对象的属性设置setter / getters。我建议你使用ES5 Object.defineProperties
。当然这只适用于现代浏览器。
var obj = function() {
...
Object.defineProperties(this, {
"test": {
"get": function() { ... },
"set": function() { ... }
}
});
}
obj.prototype.func = function() { ... }
var o = new obj;
o.test;
o.func();
答案 1 :(得分:9)
通常你想要 class 方法。 @Raynos在2011年5月7日的回答完成了工作,但是它定义了一个实例方法,而不是一个类方法。
下面说明了一个类定义,其中getter和setter是该类的一部分。这个定义很像@Raynos的答案,但代码中有两个不同之处:(1)" defineProperties()"动作已被移出构造函数。 (2)" defineProperties()"的参数已从实例对象" this"更改为构造函数的原型对象。
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);
产生这些结果:
initial Area:4
modified Area:9
虽然通常是阶级与实例之间的区别 定义只是风格问题,有目的 好的风格,有一种区别很重要的情况: memoized getter 。记忆吸气剂的目的是 这里描述:Smart/self-overwriting/lazy getters
当memoized值为to时,在类级别定义getter 适用于整个班级。例如,配置文件 应该只读一次;然后应该应用结果值 在该计划的持续时间。以下示例代码 在类级别定义一个memoized getter。
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);
产地:
memoizedConfigParam:42
从示例中可以看出,memoized getters有 getter函数删除自身的特征, 然后用一个简单的值替换自己 (大概)永远不会改变。 请注意,'可配置'必须设置为' true'。
在记忆值时定义实例级别的getter 取决于实例的内容。定义动了 在构造函数中,注意的对象是'这个'。
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);
产地:
memoizedCalculation 2:8
memoizedCalculation 3:27
如果你想保证(而不是推测)那个被记忆的 价值永远不会改变,可写的'属性需要 改变了。这使代码更复杂。
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
产地:
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
OP的原始问题,从2011年3月7日开始,呈现基本问题 getter和setter语法,注意它在一个对象上工作但是 不在&#39;这个&#39;,并询问如何在其中定义getter和setter 构造函数。除了上面的所有例子,还有 也是一个&#34;廉价拍摄&#34;这样做的方法:在其中创建一个新对象 构造函数,就像OP一样,但随后将对象分配给 成为&#39;这个&#39;的成员。所以,原始代码看起来像 这样:
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);
产地:
false
信不信由你,我实际上遇到过的情况 这个&#34; cheap-shot&#34;是最好的解决方案。具体来说,我用过这个 当我从封装在其中的几个表中获取记录时的技术 单个类,并希望呈现一个统一的视图 他们是一个名为“数据”的单一记录。
玩得开心。
IAM_AL_X
答案 2 :(得分:7)
ES6的更新 - 看看Alex Rauschmayer的书探索ES6 http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-data的第19.3.1节,其中演示了如何使用WeakMaps与getter和setter来保持私人数据。结合第16.2.2.3节http://exploringjs.com/es6/ch_classes.html#leanpub-auto-getters-and-setters会产生类似
的内容# 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
答案 3 :(得分:4)
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);
答案 4 :(得分:1)
我知道这可能会非常晚,但我想出了一种不同的方式来实现你想要的东西,为了人们,就像我一样,谷歌搜索这里的答案。
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
我已经在chrome,safari,移动游猎,firefox中进行了测试,它们都可以使用(当然是最新版本)
答案 5 :(得分:1)
@Alex我认为它更多选择和更多权力,编程是艺术,@Nat与我们分享他的发现,为此我感谢他。也许有人想这样做。
我确定setter版本是相同的,只是将g更改为s。
i.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
据说,此功能已弃用,建议不要在生产编码中使用。