So definieren Sie Setter / Getter für den Prototyp

78

BEARBEITEN Okt. 2016 : Bitte beachten Sie, dass diese Frage im Jahr 2012 gestellt wurde. Jeden Monat fügt jemand eine neue Antwort oder einen neuen Kommentar hinzu, der eine Antwort widerlegt, aber dies ist nicht wirklich sinnvoll, da die Frage wahrscheinlich veraltet ist (denken Sie daran, Es war Sache von Gnome Javascript , Gnome-Shell-Erweiterungen zu schreiben, keine Browser-Inhalte (was ziemlich spezifisch ist).

Nach meiner vorherigen Frage zur Durchführung von Unterklassen in Javascript erstelle ich eine Unterklasse aus einer Oberklasse wie folgt:

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.

Meine Frage ist, wie definiere ich mit dieser Syntax einen Setter / Getter auf dem Prototyp?

Früher habe ich gemacht:

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

    get myProperty() {
        // code.
    }
}

Aber offensichtlich funktioniert Folgendes nicht:

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

Ich verwende GJS (GNOME Javascript) und die Engine soll mehr oder weniger mit der von Mozilla Spidermonkey identisch sein. Mein Code ist nicht für einen Browser gedacht, solange er von GJS unterstützt wird (ich denke, das bedeutet Spidermonkey?), Es macht mir nichts aus, wenn er nicht kreuzkompatibel ist.

mathematisch.Kaffee
quelle
Mozilla-Dokumente erwähnen __defineGetter__und __defineSetter(aber ich habe diese nie benutzt ...). developer.mozilla.org/en/Core_JavaScript_1.5_Guide/…
bfavaretto
Fantastisch, das sieht so aus, wie ich es mir vorstelle. Wenn Sie es als Antwort posten, werde ich es akzeptieren. Prost! :)
math.coffee
Fertig und Beispiele aus MDN hinzugefügt.
Bfavaretto
1
Bitte tu das nicht. Die Vererbung in JS besteht aus drei Zeilen: Rufen Sie die Superklasse auf, setzen Sie den Prototyp auf die Superklasse und setzen Sie den Konstruktor auf die untergeordnete Klasse zurück. Das Ende. Solche Schreibmethoden sind reine Zeitverschwendung.
Hal50000
verknüpft: Getters \ Setter für Dummies
vsync

Antworten:

74

Verwenden einer Objektliteraldeklaration (einfachster Weg):

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

Verwenden Object.defineProperty(in modernen Browsern, die ES5 unterstützen):

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

Oder mit __defineGetter__und __defineSetter__( DEPRECATED ):

var d = Date.prototype;
d.__defineGetter__("year", function() { return this.getFullYear(); });
d.__defineSetter__("year", function(y) { this.setFullYear(y); });
bfavaretto
quelle
Entschuldigung, mein Fehler. Ich habe das für die neue Funktionsnotation, die in es6 eingeführt wurde, falsch verstanden:let foo = { bar () { return baz }}
royhowie
@royhowie Ich sehe, die ES5 get/ setSyntax scheint die neue Methodensyntax inspiriert zu haben :)
Bfavaretto
Ich denke nicht, dass es notwendig ist, wieder einen Funktionsnamen für den Getter zu schreiben. Die MDN-Dokumentbeispiele verwenden anonyme Funktionen.
StanE
Seien Sie auch vorsichtig mit Lambdas in Getter / Setter. thisin Lambdas bezieht sich auf globales Objekt.
Grabantot
101

Verwenden Sie Object.defineProperty()auf Subclass.prototype. Es gibt auch __defineGetter__ und __defineSetter__in einigen Browsern verfügbar, aber sie sind veraltet. Für Ihr Beispiel wäre es:

Object.defineProperty(Subclass.prototype, "myProperty", {
    get: function myProperty() {
        // code
    }
});
Bergi
quelle
1
"sie sind veraltet (genauso wie __proto__)" - beachten Sie, dass ab __proto__dem ES6-Entwurf standardisiert wird.
Fabrício Matté
@ FabrícioMatté: Ich weiß (aber ich mag es nicht). Hier ging es jedoch darum, Object.createstattdessen zu verwenden .
Bergi
Ja, ich habe nur einen Hinweis hinzugefügt, dass es nicht mehr als veraltet gilt, sobald ES6 zum Standard wird (selbst das Leck für IE11 hat es anscheinend bereits implementiert). Natürlich dauert es lange, bis wir darüber nachdenken können, es in einer realen Umgebung zu verwenden, für die wenigen Anwendungsfälle, die es möglicherweise hat.
Fabrício Matté
1
Duh! "Wie definiere ich einen Setter / Getter [Eigenschaft] für den Prototyp?" "Sie definieren es als eine Eigenschaft des Prototyps."
Johann
2
@bob Ich bin mir nicht sicher, was dein Kommentar bedeuten soll. Sicher, wenn Sie die Dauer dynamisch erhalten möchten, benötigen Sie einen Getter, aber er kann auf anderen Eigenschaften genauso funktionieren wie alle anderen Methoden:Object.defineProperty(…, "duration", { get: function() { return this.end - this.begin; }});
Bergi
39

Ich denke, Sie wollten so vorgehen:

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

Павел Щеголев
quelle
2
Tolle. Die einzig richtige Antwort erhält keine Stimmen. Es gibt Object.definePropertykeinen Grund zur Verwendung, es sei denn, Sie müssen die Beleidigung des alten IE erleiden.
Hal50000
1
@ Hal50000 Sie ignorieren, dass diese Antwort fast 4 Jahre nach dem Stellen der Frage veröffentlicht wurde.
Ohnmachtssignal
1
@faintsignal Um mich selbst zu korrigieren, gibt es einige Gründe, Object.defineProperty()anstelle des obigen Beispiels zu verwenden, wenn Sie aufzählbar, konfigurierbar, beschreibbar usw. festlegen möchten. Aber Sie haben Recht, ein Blick auf das Datum des Beitrags ist gut Idee.
Hal50000
6
Unit.prototype.constructorwird gelöscht, wenn Sie so überschreiben Unit.prototype. Diese Lösung gefällt mir allerdings sehr gut. Vielleicht könntest du es tun Object.assign(Unit.prototype, { get accreation() { ... } });.
jonS90
1
Wie @ jonS90 erwähnt, gibt es einige sehr triftige Gründe, warum diese Methode eine schlechte Idee sein kann. Sie verlieren die Klassenvererbung usw. Wenn Sie eine Klassenhierarchie verwenden, sollten Sie alternative Methoden verwenden. Beispielsweise können Sie mit dieser Methode keine Eigenschaft Fieldfestlegen.
cmroanirgo
5

Um Setter und Getter "innerhalb des Prototyps des Objekts" zu definieren, müssen Sie Folgendes tun:

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

Sie können dies mit einer Utility-Funktion kurzschließen:

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

Hier verwenden wir prop.apply, um den Kontext Product.prototype als "this" festzulegen, wenn wir ihn aufrufen.

Mit diesem Code beenden Sie mit einer get / set-Eigenschaft im Prototyp des Objekts, nicht in der Instanz, wie in der Frage gestellt.

(Getesteter Firefox 42, Chrome 45)

Mackraken
quelle
Sehr schön. Sie haben in Ihrem Beispiel jedoch keinen Setter angewendet, sodass der Versuch, einen Setter zu erstellen, im Hintergrund pr.totalfehlschlägt. In der Praxis ist es wahrscheinlich am besten, eine Setter-Funktion zu übergeben, um einen Fehler auszulösen, wenn nur ein Getter erstellt werden soll.
Ohnmachtssignal
4

Geben Sie einen Getter oder Setter in Konstruktoren mit der Object.defineProperty () -Methode an. Diese Methode verwendet drei Argumente: Das erste Argument ist das Objekt, dem die Eigenschaft hinzugefügt werden soll, das zweite ist der Name der Eigenschaft und das dritte ist der Deskriptor der Eigenschaft. Zum Beispiel können wir den Konstruktor für unser Personenobjekt wie folgt definieren:

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

Die Verwendung von Object.defineProperty () bietet mehr Kontrolle über unsere Eigenschaftsdefinition. Beispielsweise können wir angeben, ob die von uns beschriebene Eigenschaft dynamisch gelöscht oder neu definiert werden kann, ob ihr Wert geändert werden kann usw.

Wir können solche Einschränkungen vornehmen, indem wir die folgenden Eigenschaften des Deskriptorobjekts festlegen:

  • beschreibbar: Dies ist ein Boolescher Wert, der angibt, ob der Wert der Eigenschaft geändert werden kann. Der Standardwert ist false
  • konfigurierbar: Dies ist ein Boolescher Wert, der angibt, ob der Deskriptor der Eigenschaft geändert oder die Eigenschaft selbst gelöscht werden kann. Der Standardwert ist false
  • Aufzählbar: Dies ist ein Boolescher Wert, der angibt, ob auf die Eigenschaft in einer Schleife über die Eigenschaften des Objekts zugegriffen werden kann. Der Standardwert ist false
  • Wert: Dies stellt den Wert dar, der der Eigenschaft zugeordnet ist. Der Standardwert ist undefiniert
Michael Horojanski
quelle
0

Hier ist ein einfaches Beispiel für die Animal → DogVererbung mit Animaleinem Getter und einem 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

vsync
quelle