构造函数中的 getter/setter

发布于 2024-10-20 20:07:41 字数 1028 浏览 9 评论 0原文

我最近了解到可以在 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 又扩展原型呢?

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.

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.

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 not work - instance.func is undefined:

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.

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.

So how do I implement getters/setters in the constructor so as to be able to both use getters/setters and extending the prototype?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

过度放纵 2024-10-27 20:07:41

你不能那样做。

不过,您可以为对象的属性设置 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();

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.

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

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

var o = new obj;
o.test;
o.func();
断念 2024-10-27 20:07:41

通常您需要方法。 @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

尽管通常类与实例之间存在区别
定义只是风格问题,有一个目的
良好的风格,并且在某些情况下,区别很重要:
记忆化的吸气剂。记忆吸气剂的目的是
此处描述:Smart/self-overwriting/lazy getters

当存储的值是时,在类级别定义 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

如示例所示,记忆化吸气剂具有
getter 函数会删除自身的特征,
然后将其自身替换为一个简单的值
(大概)永远不会改变。
请注意,“可配置”必须设置为“true”。

当存储值时,在实例级别定义 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

如果你想保证(而不是假设)记忆的
值永远不会改变,“可写”属性需要
被改变。这使得代码变得有点复杂。

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 语法,注意到它适用于对象,但是
不是关于“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

Usually you want class methods. The answer by @Raynos on May 7, 2011 gets the job done, but it defines an instance method, not a class method.

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.

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

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.

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'.

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'.

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.

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:

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

你的往事 2024-10-27 20:07:41

ES6 更新 - 查看 Alex Rauschmayer 的书 探索 ES6 的第 19.3.1 节 http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-data 演示了如何使用带有 getter 和 setter 的 WeakMap 来保存私有数据。结合第 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

Update for ES6 -- have a look at section 19.3.1 of Alex Rauschmayer's book Exploring ES6 http://exploringjs.com/es6/ch_maps-sets.html#sec_weakmaps-private-data which 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-setters would result in something like

# 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
眼眸 2024-10-27 20:07:41
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);
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);
合久必婚 2024-10-27 20:07:41

我知道这可能已经很晚了,但我想出了一种不同的方法来完成你想要的事情,并且为了像我这样的人,在这里搜索这个问题的答案。

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 中对此进行了测试,它们都可以工作(当然是最新版本)

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)

完美的未来在梦里 2024-10-27 20:07:41

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

我确信 setter 版本是相同的,但只是将 g 更改为 s。

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

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

@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.

I'm sure the setter version is the same but just changing that g to a 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

With that said, this feature is deprecated, advices to not to use in production coding.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文