构造函数中的getter / setter

时间:2011-03-07 16:29:46

标签: 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) { 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并扩展原型?

6 个答案:

答案 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

据说,此功能已弃用,建议不要在生产编码中使用。