【问题标题】:How to define setter/getter on prototype如何在原型上定义 setter/getter
【发布时间】:2012-05-22 11:50:48
【问题描述】:

2016 年 10 月编辑:请注意,这个问题是在 2012 年提出的。每个月左右都会有人添加一个新的答案或评论来反驳答案,但这样做并没有真正意义这个问题可能已经过时了(请记住,是 Gnome Javascript 编写 gnome-shell 扩展,而不是浏览器的东西,这是非常具体的)。

按照my previous question 介绍如何在 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/…
  • 太棒了,这看起来就像我所追求的。如果您将其发布为答案,我会接受。干杯! :)
  • 完成了,并添加了来自 MDN 的示例。
  • 请不要这样做。 JS中的继承是三行:调用超类,将原型设置为超类,将构造函数重置为子类。结束。编写这样的方法完全是浪费时间。

标签: javascript getter gjs


【解决方案1】:

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

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

【讨论】:

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

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

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 我明白了,ES5 get/ set 语法似乎启发了新方法语法:)
  • 我觉得不需要再为getter写一个函数名了。 MDN 文档示例使用匿名函数。
  • 还要小心 getter/setter 中的 lambda。 lambdas 中的this 指的是全局对象。
【解决方案3】:

我认为你想这样做:

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 的侮辱。
  • @Hal50000 你忽略了这个答案是在问题提出近 4 年后发布的。
  • @faintsignal 其实,为了纠正自己,当你想设置可枚举、可配置、可写等时,有一些理由使用Object.defineProperty() 而不是上面的例子。但你是对的,看看帖子的日期是个好主意。
  • Unit.prototype.constructor 会在您像这样覆盖 Unit.prototype 时被删除。不过,我确实非常喜欢这个解决方案。也许你可以做Object.assign(Unit.prototype, { get accreation() { ... } });
  • 正如@jonS90 提到的,有一些非常正当的理由说明这种方法可能不是一个好主意。你失去了类继承等。如果你使用类层次结构,你最好使用替代方法。例如,您不能使用此方法在Field 上添加属性。
【解决方案4】:

要在“对象的原型内”定义 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 函数来引发错误。
【解决方案5】:

通过 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() 给出 更多地控制我们的属性定义。例如,我们可以指定我们的属性是否 are describe 可以动态删除或重新定义,如果它的值可以改变,等等 开。

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

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

【讨论】:

    【解决方案6】:

    这是一个简单的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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-27
      • 1970-01-01
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 2020-08-16
      相关资源
      最近更新 更多