Javascript 构造函数中的Getter/setter

Javascript 构造函数中的Getter/setter,javascript,prototype,constructor,getter-setter,Javascript,Prototype,Constructor,Getter Setter,我最近读到一个事实,即有可能在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

我最近读到一个事实,即有可能在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);
此代码始终将
转换为布尔值。因此,如果您编码
instance.test=0
,那么
instance.test===false

但是,要使其工作,您必须实际返回一个对象,这意味着新实例不是
obj
类型,而是一个普通对象。这意味着更改
obj
的原型对实例没有影响。例如,这不起作用-
实例。func
未定义:

obj.prototype.func = function() { console.log(this.value); };
因为
实例
不是类型
obj
。为了让原型函数工作,我想我不应该返回一个普通对象,而应该不返回任何东西,这样
instance
就会是
obj
类型,就像常规构造函数一样

接下来的问题是如何实现getter/setter?我只能找到描述如何将它们添加到对象的文章,而不是作为自定义类型构造函数的一部分

那么,如何在构造函数中实现getter/setter,以便既能使用getter/setter,又能扩展原型呢?

你不能这样做

不过,您可以为对象的属性设置setter/getter。不过我建议你使用ES5。当然,这只适用于现代浏览器

var obj = function() {
    ...
    Object.defineProperties(this, {
        "test": {
             "get": function() { ... },
             "set": function() { ... }
        }
    });
}

obj.prototype.func = function() { ... }

var o = new obj;
o.test;
o.func();

我知道这可能已经非常晚了,但我找到了一种不同的方法来实现你想要的,为了像我这样的人,在谷歌上搜索答案

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、mobile safari和firefox上测试过了,它们都能正常工作(当然是最新版本)

@Alex我认为这是更多的选择和更强大的功能,编程是艺术,@Nat与我们分享他的发现,为此我感谢他。也许有人想这样做

我确信setter的版本是一样的,只是把g改成了s

i、 g:

尽管如此,此功能已被弃用,建议不要在生产编码中使用。

ES6更新——请参阅Alex Rauschmayer的《探索ES6》一书的第19.3.1节,该书演示了如何使用带getter和setter的weakmap来保存私有数据。与第16.2.2.3节相结合会产生类似的结果

# 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
通常您需要类方法。@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
虽然通常是类和实例之间的区别 定义只是一个风格的问题,是有目的的 好的风格,有一种情况下,区别很重要: 记忆化的getter。记忆型getter的用途是 此处描述:

在调用memorized值时,在类级别定义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);
产生:

memoizedConfigParam:42
memoizedCalculation 2:8
memoizedCalculation 3:27
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
false
从示例中可以看出,记忆化的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);
产生:

memoizedConfigParam:42
memoizedCalculation 2:8
memoizedCalculation 3:27
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
false
如果你想保证(而不是假设)记忆 值将永远不会更改,“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
产生:

memoizedConfigParam:42
memoizedCalculation 2:8
memoizedCalculation 3:27
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
false
产生:

memoizedConfigParam:42
memoizedCalculation 2:8
memoizedCalculation 3:27
memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
false
信不信由你,我实际上遇到过这样的情况 这种“廉价”是最好的解决办法。具体来说,我用了这个 当我有几个表中的记录封装在 一个单独的类,并希望呈现一个统一的视图 它们是一个名为“数据”的单一记录

玩得开心


IAM_AL_X

这很有效。虽然打字要多一些,但还是有一个清晰的解决方案。这很好。我希望语法更简洁一点,但它可以工作。我有点希望你能像使用普通函数一样使用
this.myProp=get(){}
。这不是每次你使用新的obj时都会创建get/set函数吗?@Pointy小心这些东西。是的,但我怀疑它在几乎所有情况下都会造成明显的瓶颈。谢谢你的文章。我还没有经历过任何性能故障。@Pointy那个链接现在已经死了,你能提供一个替代方案吗?“好奇这意味着什么。”该死的,用链接缩短器来做这件事是愚蠢的。我认为这与运行时性能有关。有人问了一个关于为什么最近getter/setter函数这么慢的问题(它们没有那么慢,只是比你想象的要慢)。@Pointy这里有一个来自Internet存档的死链接的存档:我不确定它是否可以跨浏览器工作,但即使它可以,它看起来也很恶心。你有什么理由选择这条路线而不是像@Raynos那样使用
defineProperties()
。对于现代浏览器来说,合适的语法是