如何在原型上定义setter / getter


78

EDIT 2016年10月:请注意,这个问题是在2012年提出的。每个月左右,有人会添加一个新的答案或评论来驳斥该答案,但这样做实际上没有任何意义,因为该问题可能已经过时了(请记住,是Gnome Javascript编写gnome-shell扩展,而不是浏览器的东西,这是相当具体的)。

我先前关于如何在Javascript中进行子类化的问题之后,我正在制作一个超类的子类,如下所示:

function inherits(Child,Parent) {
    var Tmp = function {};
    Tmp.prototype = Parent.prototype;
    Child.prototype = new Tmp();
    Child.prototype.constructor = Child;
}
/* Define subclass */
function Subclass() {
    Superclass.apply(this,arguments);
    /* other initialisation */
}
/* Set up inheritance */
inherits(Subclass,Superclass);
/* Add other methods */
Subclass.prototype.method1 = function ... // and so on.

我的问题是,如何使用这种语法在原型上定义一个setter / getter?

我曾经做:

Subclass.prototype = {
    __proto__: Superclass.prototype,
    /* other methods here ... */

    get myProperty() {
        // code.
    }
}

但显然,以下操作无效:

Subclass.prototype.get myProperty() { /* code */ }

我使用的是GJS(GNOME Javascript),该引擎与Mozilla Spidermonkey差不多。我的代码不适合浏览器使用,只要它受GJS支持(我想这意味着Spidermonkey?),我就不在乎它是否不兼容。


Mozilla的文档提到__defineGetter____defineSetter(但我从来没有实际使用的...)。developer.mozilla.org/en/Core_JavaScript_1.5_Guide/...
bfavaretto

太棒了,看起来就像我追求的那样。如果您将其发布为答案,我会接受。干杯!:)
mathematical.coffee

完成此操作,并添加了来自MDN的示例。
bfavaretto 2012年

1
请不要这样做。JS中的继承是三行:调用超类,将原型设置为超类,并将构造函数重置为子类。结束。这样的编写方法完全浪费时间。
Hal50000

Answers:


74

使用对象文字声明(最简单的方法):

var o = {
    a: 7,
    get b() {
        return this.a + 1;
    },
    set c(x) {
        this.a = x / 2
    }
};

使用Object.defineProperty(在支持ES5的现代浏览器上):

Object.defineProperty(o, "myProperty", {
    get: function myProperty() {
        // code
    }
});

或使用__defineGetter____defineSetter__(已弃用):

var d = Date.prototype;
d.__defineGetter__("year", function() { return this.getFullYear(); });
d.__defineSetter__("year", function(y) { this.setFullYear(y); });

抱歉,是我的错。对于es6中引入的新功能符号,我误解了:let foo = { bar () { return baz }}
royhowie 2015年

@royhowie我知道,ES5 get/set语法似乎启发了新方法的语法:)
bfavaretto

我认为不需要再次为getter编写函数名称。MDN docu示例使用匿名函数。
StanE

还要小心使用吸气剂/吹洗剂中的λ。this在lambdas中是指全局对象。
抓狂

100

Object.defineProperty()在上使用Subclass.prototype。在某些浏览器中也有__defineGetter__ 并且__defineSetter__可用,但已弃用。以您的示例为例:

Object.defineProperty(Subclass.prototype, "myProperty", {
    get: function myProperty() {
        // code
    }
});

1
“它们已弃用(与相同__proto__)”-注意,__proto__从ES6草案开始,它们已经标准化。
法布里西奥磨砂

@FabrícioMatté:我知道(但是我不喜欢)。但是,这里的重点是Object.create代替使用。
Bergi

是的,我只是想补充一点,一旦ES6成为标准,它就不会被视为不推荐使用(即使IE11的漏洞显然已经实现了)。尽管当然,要想在实际环境中使用它还有很长一段时间,因为它可能只有少数用例。
法布里西奥磨砂

1
h!“如何在原型上定义设置器/获取器[属性]?” “您将其定义为原型的属性。”
约翰

2
@bob不确定您的评论应该是什么意思。当然,如果要动态获取持续时间,则需要使用吸气剂,但是它可以像其他所有方法一样在其他属性上运行:Object.defineProperty(…, "duration", { get: function() { return this.end - this.begin; }});
Bergi 2015年

38

我认为您想这样做:

function Unit() {
   	this._data; // just temp value
}
Unit.prototype = {
 	get accreation() {
   		return this._data;
   	},
   	set accreation(value) {
   		this._data = value
   	},
}
Unit.prototype.edit = function(data) {
   	this.accreation = data; // setting
   	this.out();
};

Unit.prototype.out = function() {
    alert(this.accreation); // getting
};

var unit = new Unit();
unit.edit('setting and getting');

function Field() {
    // children
}

Field.prototype = Object.create(Unit.prototype);

Field.prototype.add = function(data) {
  this.accreation = data; // setting
   	this.out();
}

var field1 = new Field();
field1.add('new value for getter&setter');

var field2 = new Field();
field2.out();// because field2 object has no setting


2
惊人。唯一正确的答案是不投票。Object.defineProperty除非您必须忍受旧IE的侮辱,否则有零使用理由。
Hal50000

1
@ Hal50000您忽略了这个答案在提出问题大约四年后发布。
faintsignal

1
@faintsignal实际上,要纠正自己,Object.defineProperty()当您要设置可枚举,可配置,可写等内容时,有一些理由可以代替上面的示例。但是,您是对的,看看帖子的日期是一个很好的选择。理念。
Hal50000

6
Unit.prototype.constructorUnit.prototype像这样覆盖时被擦除。我确实很喜欢这种解决方案。也许你可以做Object.assign(Unit.prototype, { get accreation() { ... } });
jonS90'1

1
正如@ jonS90所提到的,有很多非常合理的理由说明此方法可能是个坏主意。您将失去类继承等。如果您使用的是类层次结构,则最好使用其他方法。例如,您不能Field使用此方法设置属性。
cmroanirgo

5

要在对象的原型内部定义setter和getter,您必须执行以下操作:

Object.defineProperties(obj.__proto__, {"property_name": {get: getfn, set: setfn}})

您可以使用实用程序功能将其简化:

//creates get/set properties inside an object's proto
function prop (propname, getfn, setfn) {
    var obj = {};
    obj[propname] = { get: getfn, set: setfn };
    Object.defineProperties(this, obj);        
}

function Product () {
     this.name =  "Product";
     this.amount =  10;
     this.price =  1;
     this.discount =  0;
}

//how to use prop function
prop.apply(Product.prototype, ["total", function(){ return this.amount * this.price}]);

pr = new Product();
console.log(pr.total);

在这里,当我们调用prop.apply时,将其上下文Product.prototype设置为“ this”。

使用此代码,您将在对象的原型(而不是实例)中获得一个get / set属性,这是问题所提出的。

(经过测试的Firefox 42,Chrome 45)


非常好。不过,您没有在示例中应用设置器,因此尝试设置pr.total将无提示地失败。在实践中,最好只传递一个setter函数以在只想创建一个getter的时候抛出一个错误。
faintsignal

4

通过Object.defineProperty()方法在构造函数中指定getter或setter。此方法带有三个参数:第一个参数是要添加属性的对象,第二个参数是属性的名称,第三个参数是属性的描述符。例如,我们可以为我们的person对象定义构造函数,如下所示:

var Employee = (function() {
    function EmployeeConstructor() {
        this.first = "";
        this.last = "";
        Object.defineProperty(
            this,
            "fullName", {
                get: function() {
                    return this.first + " " +
                        this.last;
                },
                set: function(value) {
                    var parts = value.toString().split(" ");
                    this.name = parts[0] || "";
                    this.last = parts[1] || "";
                }
            });
    }
    return
    EmployeeConstructor;
}());

使用Object.defineProperty()可以更好地控制属性定义。例如,我们可以指定所描述的属性是否可以动态删除或重新定义,是否可以更改其值,等等。

我们可以通过设置描述符对象的以下属性来约束:

  • 可写的:这是一个布尔值,指示是否可以更改属性的值;其默认值为false
  • 可配置的:这是一个布尔值,指示是否可以更改属性的描述符或可以删除​​属性本身;其默认值为false
  • 枚举:这是一个布尔值,指示是否可以在对象属性的循环中访问该属性;其默认值为false
  • value:这表示与属性关联的值;其默认值为undefined

0

这是一个简单的Animal → Dog继承示例,Animal具有一个getter和一个setter

//////////////////////////////////////////
// General Animal constructor
function Animal({age, name}) {
  // if-statements prevent triggering the setter on initialization
  if(name) this.name = name
  if(age) this.age = age
}

// an alias "age" must be used, so the setter & getter can use an
// alternative variable, to avoid using "this.age", which will cause
// a stack overflow of "infinite" call stack when setting the value.
Object.defineProperty(Animal.prototype, "age", {
  get(){
    console.log("Get age:", this.name, this._age) // getting
    return this._age
  },
  set(value){
    this._age = value
    console.log("Set age:", this.name, this._age) // setting
  }
})




//////////////////////////////////////////
// Specific Animal (Dog) constructor
function Dog({age = 0, name = 'dog'}) {
  this.name = name
  this.age = age
}

// first, defined inheritance
Dog.prototype = new Animal({});

// add whatever additional methods to the prototype of Dog
Object.assign(Dog.prototype, {
  bark(woff){
    console.log(woff)
  }
})


//////////////////////////////////////////
// Instanciating
var koko = new Animal({age:300, name:'koko'})
var dog1 = new Dog({age:1, name:'blacky'})
var dog2 = new Dog({age:5, name:'shorty'})

console.log(dog1)
koko.age
dog1.age = 3;
dog1.age
dog2.age

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.