如何在原型上定义 setter/getter

IT技术 javascript getter gjs
2021-01-20 00:16:08

编辑 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?),我不介意它是否不交叉兼容。

6个回答

使用Object.defineProperty()Subclass.prototype在某些浏览器上也有__defineGetter__ 并且__defineSetter__可用,但它们已被弃用。对于您的示例,它将是:

Object.defineProperty(Subclass.prototype, "myProperty", {
    get: function myProperty() {
        // code
    }
});
是的,我只是添加了一条说明,一旦 ES6 成为标准,它就不会被视为弃用(即使 IE11 的泄漏显然已经实现了)。当然,对于它可能具有的少数用例,我们还需要很长时间才能考虑在现实世界环境中使用它。
2021-03-16 00:16:08
@FabrícioMatté:我知道(但我不喜欢)。然而,这里的重点是使用Object.create
2021-04-05 00:16:08
呸!“我如何在原型上定义一个 setter/getter [属性]?” “您将其定义为原型上的属性。”
2021-04-06 00:16:08
“它们已被弃用(就像__proto__)” - 请注意,__proto__从 ES6 草案开始,它已被标准化。
2021-04-07 00:16:08
@bob 不确定您的评论是什么意思。当然,如果您想动态获取持续时间,您将需要一个 getter,但它可以像所有其他方法一样对其他属性进行操作:Object.defineProperty(…, "duration", { get: function() { return this.end - this.begin; }});
2021-04-07 00:16:08

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

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); });
我认为不需要再次为 getter 编写函数名称。MDN 文档示例使用匿名函数。
2021-03-26 00:16:08
抱歉,是我的错。我误读了 es6 中引入的新函数符号:let foo = { bar () { return baz }}
2021-03-29 00:16:08
@royhowie 我明白了,ES5 get/set语法似乎启发了新的方法语法 :)
2021-04-03 00:16:08
还要小心 getter/setter 中的 lambdas。this在 lambdas 中是指全局对象。
2021-04-10 00:16:08

我想你想这样做:

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

惊人。唯一正确的答案没有选票。Object.defineProperty除非您不得不忍受旧 IE 的侮辱,否则使用零理由
2021-03-18 00:16:08
@faintsignal 其实,纠正我自己,Object.defineProperty()当你想设置可枚举,可配置,可写等时,有一些理由使用而不是上面的例子。但是你是对的,看看帖子的日期是好的主意。
2021-03-30 00:16:08
正如@jonS90 所提到的,有一些非常有效的理由说明这种方法可能是个坏主意。你失去了类继承等。如果你使用类层次结构,你最好使用替代方法。例如,您不能Field使用此方法放置属性
2021-04-01 00:16:08
Unit.prototype.constructorUnit.prototype像这样覆盖时会被删除不过我非常喜欢这个解决方案。也许你可以做Object.assign(Unit.prototype, { get accreation() { ... } });
2021-04-02 00:16:08
@Hal50000 您忽略了这个答案是在提出问题近 4 年后发布的。
2021-04-13 00:16:08

要在“对象原型内部”定义 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)

非常好。但是,您没有在示例中应用 setter,因此尝试设置pr.total会静默失败。在实践中,当一个人只想创建一个 getter 时,最好传递一个 setter 函数来抛出错误。
2021-04-07 00:16:08

通过 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() 可以更好地控制我们的属性定义。例如,我们可以指定我们所描述的属性是否可以动态删除或重新定义,是否可以更改其值,等等。

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

  • writable:这是一个布尔值,表示属性的值是否可以更改;它的默认值为 false
  • 可配置:这是一个布尔值,表示是否可以更改属性的描述符或可以删除​​属性本身;它的默认值为 false
  • enumerable:这是一个布尔值,指示是否可以在对象属性的循环中访问该属性;它的默认值为 false
  • value:这表示与属性相关的值;它的默认值是未定义的