Javascript 构造函数中的 getter/setter
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5222209/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Getter/setter in constructor
提问by pimvdb
I recently read about the fact that there is a possibility of defining getters/setters in JavaScript. It seems extremely helpful - the setter is a kind of 'helper' which can parse the value to be set first, before actually setting it.
我最近读到了一个事实,即可以在 JavaScript 中定义 getter/setter。这似乎非常有用 - setter 是一种“助手”,它可以在实际设置之前先解析要设置的值。
For example, I currently have this code:
例如,我目前有这个代码:
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);
This code always converts value
to a boolean. So if you code instance.test = 0
, then instance.test === false
.
此代码始终转换value
为布尔值。所以如果你编码instance.test = 0
,那么instance.test === false
.
However, for this to work you have to actually return an object, which means that the new instance is not of type obj
but just is a plain object. This means that changing the prototype of obj
has no effect on instances. For example, this does notwork - instance.func
is undefined:
但是,要使其正常工作,您必须实际返回一个object,这意味着新实例不是类型obj
而是一个普通对象。这意味着更改原型obj
对实例没有影响。举例来说,这样做不工作-instance.func
是不确定的:
obj.prototype.func = function() { console.log(this.value); };
because instance
is not of type obj
. To get the prototype functions work, I guess I should not return a plain object, but rather not return anything so that instance
would just be of type obj
, like a regular constructor works.
因为instance
不是类型obj
。为了让原型函数工作,我想我不应该返回一个普通对象,而是不返回任何东西,这样instance
它就只是 type obj
,就像一个普通的构造函数一样。
The problem then is how to implement getters/setters? I can only find articles describing how to add these to an object, not as being part of the constructor of a custom type.
那么问题是如何实现getter/setter?我只能找到描述如何将这些添加到对象的文章,而不是作为自定义类型构造函数的一部分。
So how do I implement getters/setters in the constructor so as to be able to both use getters/setters and extending the prototype?
那么如何在构造函数中实现 getter/setter 以便能够同时使用 getter/setter 和扩展原型?
回答by Raynos
You can't do that.
你不能那样做。
You can set setter/getters for properties of objects though. I advice you use ES5 Object.defineProperties
though. of course this only works in modern browsers.
不过,您可以为对象的属性设置 setter/getter。不过我建议你使用 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();
回答by IAM_AL_X
Usually you want classmethods. The answer by @Raynos on May 7, 2011 gets the job done, but it defines an instancemethod, not a class method.
通常你想要类方法。@Raynos 在 2011 年 5 月 7 日的回答完成了工作,但它定义了一个实例方法,而不是一个类方法。
The following illustrates a class definition with a the getter and setter being part of the class. This definition is a lot like the answer by @Raynos, but with two differences in the code: (1) The "defineProperties()" action has been moved out of the constructor. (2) The argument to "defineProperties()"as been changed from the instance object "this", to the constructor's prototype object.
下面说明了一个类定义,其中 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);
Which produces these results:
产生这些结果:
initial Area:4
modified Area:9
Although usually the distinction between class versus instance definition is just a matter of style, there is a purpose to good style, and there is a case where the distinction matters: the memoized getter. The purpose for a memoized getter is described here: Smart/self-overwriting/lazy getters
尽管通常类定义与实例定义之间的区别只是风格问题,但良好的风格是有目的的,在某些情况下,这种区别很重要:记忆化的 getter。这里描述了记忆化 getter 的目的:Smart/self-overwriting/lazy getters
Define the getter at the class level when the memoized value is to pertain to the entire class. For example, a configuration file should be read only once; the resulting values should then apply for the duration of the program. The following sample code defines a memoized getter at the class level.
当记忆值与整个类有关时,在类级别定义 getter。例如,一个配置文件应该只读一次;结果值应适用于该计划的持续时间。以下示例代码在类级别定义了一个记忆化的 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);
Produces:
产生:
memoizedConfigParam:42
As can be seen in the example, memoized getters have the characteristic that the getter function deletes itself, then replaces itself with a simple value that (presumably) will never change. Note that 'configurable' must be set to 'true'.
从示例中可以看出,记忆化的 getter 具有 getter 函数删除自身的特性,然后将自身替换为一个(大概)永远不会改变的简单值。请注意,“可配置”必须设置为“真”。
Define the getter at the instance level when the memoized value depends upon the contents of instance. The definition moves inside the constructor, and the object of attention is 'this'.
当记忆值取决于实例的内容时,在实例级别定义 getter。定义在构造函数内部移动,关注的对象是“this”。
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);
Produces:
产生:
memoizedCalculation 2:8
memoizedCalculation 3:27
If you want to guarantee (rather than presume) that the memoized value will never be changed, the 'writable' attribute needs to be changed. That makes the code a bit more complicated.
如果您想保证(而不是假设)记忆值永远不会改变,则需要更改 'writable' 属性。这使得代码有点复杂。
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
Produces:
产生:
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
The OP's original question, from March 7, 2011, presented basic getter and setter syntax, noted that it worked on an object but not on 'this', and asked how to define getters and setters within a constructor. In addition to all the examples above, there is also a "cheap-shot" way of doing it: create a new object within the constructor, like the OP did, but then assign the object to be a member within 'this'. So, the original code would look like this:
OP 的原始问题,从 2011 年 3 月 7 日开始,提出了基本的 getter 和 setter 语法,指出它适用于对象而不是“this”,并询问如何在构造函数中定义 getter 和 setter。除了上面的所有示例之外,还有一种“便宜”的方法:在构造函数中创建一个新对象,就像 OP 所做的那样,然后将该对象分配为“this”中的成员。因此,原始代码如下所示:
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);
Produces:
产生:
false
Believe it or not, I have actually run into situations where this "cheap-shot" is the best solution. Specifically, I used this technique when I had records from several tables encapsulated within a single class, and wanted to present a unified view as though they were a single record called 'data'.
信不信由你,我实际上遇到过这种“便宜货”是最佳解决方案的情况。具体来说,当我将多个表中的记录封装在一个类中时,我使用了这种技术,并希望呈现一个统一的视图,就好像它们是一个名为“数据”的单个记录一样。
Have fun.
玩得开心。
IAM_AL_X
IAM_AL_X
回答by Jeff
Update for ES6 -- have a look at section 19.3.1 of Alex Rauschmayer's book Exploring ES6http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-datawhich demonstrates how to use WeakMaps with getters and setters to hold private data. Combining with section 16.2.2.3 http://exploringjs.com/es6/ch_classes.html#leanpub-auto-getters-and-setterswould result in something like
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
回答by Umit Silwal Khatri
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);
回答by Akinos
I know this might be extremely late but I figured out a different way to accomplish what you want and for the sake of people, like myself, googling for an answer to this here it is.
我知道这可能太晚了,但我想出了一种不同的方法来完成你想要的,为了像我这样的人,在这里搜索答案。
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
I've tested this in chrome, safari, mobile safari, firefox and they all work (latest versions of course)
我已经在 chrome、safari、mobile safari、firefox 中测试过这个,它们都可以工作(当然是最新版本)
回答by user3552042
@Alex I see it as more option and more power, programming is art, @Nat share his finding with us, and for that I thank him. Maybe someone want to do it that way.
@Alex 我认为它更多的选择和更多的力量,编程是艺术,@Nat 与我们分享他的发现,为此我感谢他。也许有人想这样做。
I'm sure the setter version is the same but just changing that g to a s.
我确定 setter 版本是相同的,但只是将 g 更改为 s。
i.g:
ig:
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
With that said, this feature is deprecated, advices to not to use in production coding.
话虽如此,此功能已弃用,建议不要在生产编码中使用。