如何在原型上定义 setter/getter

编辑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) ,这个引擎应该与 MozillaSpiderMonkey 差不多。我的代码不是为浏览器设计的,只要 GJS 支持就行(我猜这意味着 SpiderMonkey?),我不介意,如果它不是交叉兼容。

66504 次浏览

Use Object.defineProperty() on Subclass.prototype. There are also __defineGetter__ and __defineSetter__ available on some browsers, but they are deprecated. For your example, it would be:

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

Using an object literal declaration (simplest way):

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

Using Object.defineProperty (on modern browsers that support ES5):

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

Or using __defineGetter__ and __defineSetter__ (DEPRECATED):

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

To define setters and getters "inside the object's prototype" you have to do something like this:

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

You can short that down with an utility function:

//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);

Here we use prop.apply to set the context Product.prototype as "this" when we call it.

With this code you end with a get/set property inside the object's prototype, not the instance, as the question asked.

(Tested Firefox 42, Chrome 45)

I think you wanted to do this way:

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

Specify a getter or a setter in constructors by Object.defineProperty() method. This method takes three arguments: the first argument is the object to add the property to, the second is the name of the property, and the third is the property's descriptor. For instance, we can define the constructor for our person object as follows:

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;
}());

Using Object.defineProperty() gives more control over our property definition. For example, we can specify if the property we are describing can be dynamically deleted or redefined, if its value can be changed, and so on.

We can such constraints by setting the following properties of the descriptor object:

  • writable: This is a Boolean that says whether the value of the property can be changed; its default value is false
  • configurable: This is a Boolean that says whether the property's descriptor can be changed or the property itself can be deleted; its default value is false
  • enumerable: This is a Boolean indicating whether the property can be accessed in a loop over the object's properties; its default value is false
  • value: This represents the value associated to the property; its default value is undefined

Here's a simple example of Animal → Dog inheritance, with Animal having a getter and a 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