通常你需要类方法。@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。此处描述了 memoized getter 的目的:Smart/self-overwriting/lazy getter
当记忆值与整个类相关时,在类级别定义 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 getter 的特点是 getter 函数会删除自己,然后用一个(可能)永远不会改变的简单值替换自己。请注意,“可配置”必须设置为“真”。
当记忆值取决于实例的内容时,在实例级别定义 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);
产生:
memoizedCalculation 2:8
memoizedCalculation 3:27
如果你想保证(而不是假设)记忆值永远不会改变,'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
产生:
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
从 2011 年 3 月 7 日起,OP 的原始问题介绍了基本的 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);
产生:
false
信不信由你,我实际上遇到了这种“廉价”是最佳解决方案的情况。具体来说,当我将多个表中的记录封装在一个类中时,我使用了这种技术,并希望呈现一个统一的视图,就好像它们是一个称为“数据”的记录一样。
玩得开心。
IAM_AL_X