Вопрос по javascript – Как определить сеттер / геттер по прототипу

70

EDIT Oct 2016Обратите внимание, что этот вопрос был задан в 2012 году. Каждый месяц или около того кто-то добавляет новый ответ или комментарий, который опровергает ответ, но на самом деле это не имеет смысла, поскольку вопрос, вероятно, устарел (помните, что это было дляGnome Javascript писать расширения оболочки gnome, а не вещи браузера, что довольно специфично).

Следующиймой предыдущий вопрос о том, как сделать подклассы в 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.

Мой вопросhow do I define a setter/getter on the prototype with this syntax?

Обычно я делал:

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

    get myProperty() {
        // code.
    }
}

Но очевидно, что следующее не сработает:

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

Я использую GJS (GNOME Javascript), и предполагается, что этот движок более или менее такой же, как и Mozilla Spidermonkey. Мой код не предназначен для браузера, поэтому, пока он поддерживается GJS (я полагаю, что это означает Spidermonkey?), Я не возражаю, если он не является кросс-совместимым.

Сделайте это и добавьте примеры из MDN. bfavaretto
Фантастика, это похоже на то, что я ищу. Если вы отправите это как ответ, я приму это. ура! :) mathematical.coffee
Мозилла упоминает документы__defineGetter__ а также__defineSetter (но я никогда не использовал их ...). Developer.mozilla.org / о / Core_JavaScript_1.5_Guide / ... bfavaretto
Пожалуйста, не делай этого. Наследование в JS состоит из трех строк: вызов суперкласса, установка прототипа в суперкласс и сброс конструктора обратно в дочерний класс. Конец. Написание таких методов - пустая трата времени. Hal50000

Ваш Ответ

5   ответов
65

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
@ royhowie Я вижу, ES5get/ setинтаксис @, кажется, вдохновил новый синтаксис метода:) bfavaretto
Я не думаю, что нужно снова писать имя функции для получателя. Примеры документов MDN используют анонимные функции. StanE
Также будьте осторожны с лямбдами в методах получения / установки.this в лямбдах относится к глобальному объекту. grabantot
91

ИспользуйтеObject.defineProperty() наSubclass.prototype. Это также__defineGetter__ а также__defineSetter__ доступно в некоторых браузерах, но не рекомендуется. Для вашего примера это будет:

Object.defineProperty(Subclass.prototype, "myProperty", {
    get: function myProperty() {
        // code
    }
});
Да, я просто добавил примечание, что оно не будет считаться устаревшим, когда ES6 станет стандартом (даже утечка для IE11, по-видимому, уже реализована). Хотя, конечно, у нас есть много времени, прежде чем мы сможем подумать об использовании его в реальной среде, для тех немногих вариантов использования, которые могут иметь место. Fabrício Matté
"они устарели (так же, как__proto__)" - Обратите внимание, что__proto__ стандартизируется с версии ES6. Fabrício Matté
@ FabrícioMatté: я знаю (но мне это не нравится). Тем не менее, смысл в том, чтобы использоватьObject.create вместо. Bergi
Duh! «Как определить сеттер / геттер [свойство] в прототипе?» «Вы определяете его как свойство прототипа.» Johann
@ bob Не уверен, что ваш комментарий должен означать. Конечно, если вы хотите получить продолжительность динамически, вам понадобится геттер, но он может работать с другими свойствами так же, как и все другие методы:Object.defineProperty(…, "duration", { get: function() { return this.end - this.begin; }}); Bergi
4

вы должны сделать что-то вроде этого:

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 молча провалится. На практике, вероятно, лучше всего передать функцию установки, чтобы выдать ошибку, когда нужно только создать метод получения. faintsignal
31

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

@ Hal50000 Вы игнорируете, что этот ответ был опубликован почти через 4 года после того, как вопрос был задан. faintsignal
@ faintsignal На самом деле, чтобы исправить себя, есть несколько причин использоватьObject.defineProperty() вместо приведенного выше примера, когда вы хотите установить перечислимое, настраиваемое, доступное для записи и т. д. Но вы правы, взглянуть на дату публикации - хорошая идея. Hal50000
Удивительно. Единственно правильный ответ не получает голосов. Существует НОЛЬ причина использоватьObject.defineProperty если тебе не придется терпеть оскорбление старого IE. Hal50000
Unit.prototype.constructor стирается при перезаписиUnit.prototype так. Мне очень нравится это решение, хотя. Может быть, вы могли бы сделатьObject.assign(Unit.prototype, { get accreation() { ... } });. jonS90
as @ jonS90 упоминает, что есть некоторыеочен веские причины, почему этот метод может быть плохой идеей. Вы теряете наследование классов и т. Д. Было бы лучше использовать альтернативные методы, если вы используете иерархию классов. например, выне могу наделить собственностьField используя этот метод. cmroanirgo
3

Object.defineProperty (). Этот метод принимает три аргумента: первый аргумент - это объект, к которому добавляется свойство, второй - имя свойства, а третий - дескриптор свойства. Например, мы можем определить конструктор для нашего объекта 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;
}());

Using Object.defineProperty () дает больше контроля над определением нашего свойства. Например, мы можем указать, может ли описываемое нами свойство динамически удаляться или переопределяться, может ли быть изменено его значение и т.

Мы можем создать такие ограничения, задав следующие свойства объекта дескриптора:

writable: это логическое значение, которое указывает, можно ли изменить значение свойства; значение по умолчанию - falseconfigurable: это логическое значение, которое указывает, можно ли изменить дескриптор свойства или удалить само свойство; значение по умолчанию - falseenumerable: это логическое значение, указывающее, можно ли получить доступ к свойству в цикле над свойствами объекта; значение по умолчанию - falsevalue: представляет значение, связанное со свойством; его значение по умолчанию не определено

Похожие вопросы