构造函数中的getter/setter

pim*_*vdb 31 javascript constructor prototype getter-setter

我最近读到了一个事实,即有可能在JavaScript中定义getter/setter.它似乎非常有用 - setter是一种'helper',可以在实际设置之前解析要设置的值.

例如,我目前有这个代码:

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);
Run Code Online (Sandbox Code Playgroud)

此代码始终转换value为布尔值.所以,如果你编码instance.test = 0,那么instance.test === false.

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

obj.prototype.func = function() { console.log(this.value); };
Run Code Online (Sandbox Code Playgroud)

因为instance不是类型obj.为了使原型函数工作,我想我不应该返回一个普通的对象,而是不返回任何东西,instance只有类型obj,就像常规构造函数一样.

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

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

Ray*_*nos 47

你不能这样做.

您可以为对象的属性设置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();
Run Code Online (Sandbox Code Playgroud)

  • 这很棒.我希望语法有点干净,但它有效.我希望你可以像正常的函数那样做`this.myProp = get(){}. (7认同)

IAM*_*L_X 10

通常你想要方法.@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);
Run Code Online (Sandbox Code Playgroud)

产生这些结果:

initial  Area:4
modified Area:9
Run Code Online (Sandbox Code Playgroud)

虽然通常类和实例定义之间的区别仅仅是一种风格问题,但是有一种良好风格的目的,并且存在区分重要的情况:记忆的吸气剂.这里描述了一个memoized getter的目的:智能/自我覆盖/懒惰的getter

当memoized值与整个类相关时,在类级别定义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);
Run Code Online (Sandbox Code Playgroud)

生产:

memoizedConfigParam:42
Run Code Online (Sandbox Code Playgroud)

从示例中可以看出,memoized getter具有getter函数删除自身的特性,然后用一个简单的值替换它自己(可能)永远不会改变.请注意,'configurable'必须设置为'true'.

当memoized值取决于实例的内容时,在实例级别定义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);
Run Code Online (Sandbox Code Playgroud)

生产:

memoizedCalculation 2:8
memoizedCalculation 3:27
Run Code Online (Sandbox Code Playgroud)

如果您想保证(而不是假设)永远不会更改memoized值,则需要更改'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
Run Code Online (Sandbox Code Playgroud)

生产:

memoizedCalculation:8
>Uncaught TypeError: Cannot assign to read only property 'memoizedCalculation' of object '#<TheConstructorJ>'
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

生产:

false
Run Code Online (Sandbox Code Playgroud)

信不信由你,我实际上遇到过这种"廉价拍摄"是最佳解决方案的情况.具体来说,当我将来自几个表中的记录封装在一个类中时,我使用了这种技术,并希望提供一个统一的视图,就像它们是一个名为"data"的记录一样.

玩得开心.

IAM_AL_X


Jef*_*eff 7

更新ES6 - 看看Alex Rauschmayer的书探索ES6的第 19.3.1节http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-data,它演示了如何将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
Run Code Online (Sandbox Code Playgroud)