Getters \ Setter für Dummies

133

Ich habe versucht , meinen Kopf um Getter und Setter und sein nicht versinken in bekommen. Ich habe gelesen , JavaScript Getter und Setter und Definieren von Getter und Setter und einfach nicht zu bekommen.

Kann jemand klar sagen:

  1. Was für ein Getter und Setter sollen und
  2. Geben Sie einige sehr einfache Beispiele?
Alexander Abakumov
quelle
12
Persönlich weiß ich nicht, wie Sie eine klarere Erklärung als Johns bekommen können ...
Jason Bunting
In der OO-Welt helfen Getter und Setter beim Schreiben einer Klasse, die ordnungsgemäß gekapselt ist
Übertausch
Grundsätzlich sehe ich das so: Sie definieren Überladungen zum Abrufen und Festlegen der Eigenschaft, und diese Überladungen sind Funktionen. aber du musst sie nicht anrufen. Auf diese Weise Sie ersetzen können a = setValue(5);mit a = 5;und setValue()würde damit unter der Haube genannt werden , zu tun , was Sie wollen.
Andrew

Antworten:

102

Zusätzlich zur Antwort von @ millimoose können Setter auch zum Aktualisieren anderer Werte verwendet werden.

function Name(first, last) {
    this.first = first;
    this.last = last;
}

Name.prototype = {
    get fullName() {
        return this.first + " " + this.last;
    },

    set fullName(name) {
        var names = name.split(" ");
        this.first = names[0];
        this.last = names[1];
    }
};

Jetzt können Sie festlegen fullName, firstund lastwird aktualisiert und umgekehrt.

n = new Name('Claude', 'Monet')
n.first # "Claude"
n.last # "Monet"
n.fullName # "Claude Monet"
n.fullName = "Gustav Klimt"
n.first # "Gustav"
n.last # "Klimt"
Matthew Crumley
quelle
2
@Akash: Nein, obwohl Internet Explorer 9 die neuere Object.definePropertyFunktion unterstützt, mit der Getter und Setter definiert werden können.
Matthew Crumley
9
Ist es nicht wirklich schmerzhaft, dass MS JS nicht richtig unterstützt und sie ihr Silverlight nicht überall laufen lassen, also muss ich alles zweimal programmieren, eines für SL und eines für den Rest der Welt :)
Akash Kava
2
@ Martin: Sie könnten sie privat machen, indem Sie dieselbe Technik wie in Johns Antwort verwenden . Wenn Sie echte Getter / Setter verwenden möchten, müssen Sie this.__defineGetter__die neuere Object.definePropertyFunktion verwenden.
Matthew Crumley
1
Nur ein Problem mit dem oben aufgeführten Ansatz: Wenn Sie Getter und Setter für bereits vorhandene Klassen hinzufügen möchten, werden Prototypen überschrieben, und auf die ursprünglichen Methoden kann nicht zugegriffen werden.
xchg.ca
1
Überschreibt dieser Ansatz nicht Name.prototype.constructor? Scheint eine schlechte Alternative zu Millimooses Antwort zu sein .
r0estir0bbe
62

Getter und Setter in JavaScript

Überblick

Getter und Setter in JavaScript werden zum Definieren berechneter Eigenschaften oder Accessoren verwendet . Eine berechnete Eigenschaft verwendet eine Funktion, um einen Objektwert abzurufen oder festzulegen. Die Grundtheorie macht ungefähr so:

var user = { /* ... object with getters and setters ... */ };
user.phone = '+1 (123) 456-7890'; // updates a database
console.log( user.areaCode ); // displays '123'
console.log( user.area ); // displays 'Anytown, USA'

Dies ist nützlich, um beim Zugriff auf eine Eigenschaft automatisch Dinge hinter den Kulissen zu erledigen, z. B. Zahlen im Bereich zu halten, Zeichenfolgen neu zu formatieren, Ereignisse mit Wertänderungen auszulösen, relationale Daten zu aktualisieren, Zugriff auf private Eigenschaften zu gewähren und vieles mehr.

Die folgenden Beispiele zeigen die grundlegende Syntax, obwohl sie einfach den internen Objektwert abrufen und festlegen, ohne etwas Besonderes zu tun. In realen Fällen würden Sie den Eingabe- und / oder Ausgabewert wie oben angegeben an Ihre Anforderungen anpassen.

Schlüsselwörter abrufen / festlegen

ECMAScript 5 unterstützt getund setSchlüsselwörter zum Definieren berechneter Eigenschaften. Sie funktionieren mit allen modernen Browsern außer IE 8 und niedriger.

var foo = {
    bar : 123,
    get bar(){ return bar; },
    set bar( value ){ this.bar = value; }
};
foo.bar = 456;
var gaz = foo.bar;

Benutzerdefinierte Getter und Setter

getund setsind keine reservierten Wörter, daher können sie überladen werden, um Ihre eigenen benutzerdefinierten, browserübergreifend berechneten Eigenschaftsfunktionen zu erstellen. Dies funktioniert in jedem Browser.

var foo = {
    _bar : 123,
    get : function( name ){ return this[ '_' + name ]; },
    set : function( name, value ){ this[ '_' + name ] = value; }
};
foo.set( 'bar', 456 );
var gaz = foo.get( 'bar' );

Oder für einen kompakteren Ansatz kann eine einzelne Funktion verwendet werden.

var foo = {
    _bar : 123,
    value : function( name /*, value */ ){
        if( arguments.length < 2 ){ return this[ '_' + name ]; }
        this[ '_' + name ] = value;
    }
};
foo.value( 'bar', 456 );
var gaz = foo.value( 'bar' );

Vermeiden Sie so etwas, da dies zu einem Aufblähen des Codes führen kann.

var foo = {
    _a : 123, _b : 456, _c : 789,
    getA : function(){ return this._a; },
    getB : ..., getC : ..., setA : ..., setB : ..., setC : ...
};

In den obigen Beispielen werden die internen Eigenschaftsnamen mit einem Unterstrich abstrahiert, um Benutzer davon abzuhalten, einfach foo.barvs. zu tun foo.get( 'bar' )und einen "ungekochten" Wert zu erhalten. Sie können bedingten Code verwenden, um abhängig vom Namen der Eigenschaft, auf die zugegriffen wird (über den nameParameter), verschiedene Aufgaben auszuführen .

Object.defineProperty ()

Die Verwendung Object.defineProperty()ist eine weitere Möglichkeit, Getter und Setter hinzuzufügen, und kann für Objekte verwendet werden, nachdem sie definiert wurden. Es kann auch verwendet werden, um konfigurierbare und aufzählbare Verhaltensweisen festzulegen. Diese Syntax funktioniert auch mit IE 8, aber leider nur für DOM-Objekte.

var foo = { _bar : 123 };
Object.defineProperty( foo, 'bar', {
    get : function(){ return this._bar; },
    set : function( value ){ this._bar = value; }
} );
foo.bar = 456;
var gaz = foo.bar;

__defineGetter __ ()

Schließlich __defineGetter__()ist eine andere Option. Es ist veraltet, wird aber im Internet immer noch häufig verwendet und wird daher wahrscheinlich nicht so schnell verschwinden. Es funktioniert in allen Browsern außer IE 10 und darunter. Obwohl die anderen Optionen auch auf Nicht-IE gut funktionieren, ist diese nicht so nützlich.

var foo = { _bar : 123; }
foo.__defineGetter__( 'bar', function(){ return this._bar; } );
foo.__defineSetter__( 'bar', function( value ){ this._bar = value; } );

Erwähnenswert ist auch, dass in den letzteren Beispielen die internen Namen von den Zugriffsnamen abweichen müssen, um eine Rekursion zu vermeiden (dh foo.barAufrufen , Aufrufen foo.get(bar), foo.barAufrufen)foo.get(bar) ...).

Siehe auch

MDN get , set , Object.defineProperty () , __defineGetter __ () , __defineSetter __ ()
MSDN IE8 Getter Support

Beejor
quelle
1
Im kompakten Ansatz , this[ '_' + name ] = value;könnte this[ '_' + name ] = arguments[1];und es gäbe keine Notwendigkeit zu geben sein valuearg.
Redhart
1
Das Beispiel: var foo = { bar : 123, get bar(){ return bar; }, set bar( value ){ this.bar = value; } }; foo.bar = 456; Löst eine Ausnahme aus: Nicht erfasster RangeError: Die maximale Größe des Aufrufstapels wurde in der Object.set-Leiste [als Balken] (<anonym>: 4: 32) in der Object.set-Leiste [als Balken] (<anonym>: 4: 32) überschritten ) an der Object.set-Leiste [als Leiste] (<anonym>: 4: 32) an der Object.set-Leiste [als Leiste] (<anonymous>: 4: 32) an der Object.set-Leiste [als Leiste] (<anonymous>) : 4: 32) bei Object.set bar [als bar] (<anonymous>: 4: 32)
nevf
1
Der Name set / get muss sich vom Eigenschaftsnamen unterscheiden. Also anstelle von bar: 123und this.bar = valueetc. ändern Sie diese zum _bar Beispiel. Siehe: hongkiat.com/blog/getters-setters-javascript
nevf
@nevf Danke für die Korrektur! Ja, normalerweise wird bei berechneten Eigenschaften die "echte" interne wie _foooder benannt mFoo. Wenn es dasselbe ist wie der Getter / Setter, verursacht es aufgrund der Rekursion eine Endlosschleife und dann einen Stack Overflow ™ ;-), denn wenn Sie a = b sagen, ruft es a.get (b) auf, das selbst a = b aufruft , der a.get (b) nennt, ...
Beejor
58

Sie würden sie beispielsweise verwenden, um berechnete Eigenschaften zu implementieren.

Beispielsweise:

function Circle(radius) {
    this.radius = radius;
}

Object.defineProperty(Circle.prototype, 'circumference', {
    get: function() { return 2*Math.PI*this.radius; }
});

Object.defineProperty(Circle.prototype, 'area', {
    get: function() { return Math.PI*this.radius*this.radius; }
});

c = new Circle(10);
console.log(c.area); // Should output 314.159
console.log(c.circumference); // Should output 62.832

(CodePen)

Millimoose
quelle
Ok, ich glaube ich fange an es zu verstehen. Ich versuche, der length-Eigenschaft eines Array-Objekts einen Getter zuzuweisen, erhalte jedoch die folgende Fehlermeldung: "Redlarlaration of var length". Der Code sieht folgendermaßen aus: obj = []; obj .__ defineGetter __ ('length', function () {return this.length;});
1
Dies liegt daran, dass Array-Objekte bereits über eine integrierte Längeneigenschaft verfügen. Wenn die erneute Deklaration erlaubt wäre, würde das Aufrufen der neuen Länge unendlich lange dauern. Versuchen Sie, die Eigenschaft "my_length" oder ähnliches aufzurufen.
Millimoose
Verwenden Sie, um beide Getter in einer Anweisung zu definieren Object.defineProperties.
r0estir0bbe
Kannst du nicht einfach {"area": ​​function () {return ...}} machen? einfach als Objekteigenschaft zuweisen
Weisen Sie RegarBoy
@developer Das ist kein Javascript-Getter im Sinne der Sprache, das ist nur eine Funktion. Sie müssen es aufrufen, um den Wert zu erhalten. Der Zugriff auf die Eigenschaft wird dadurch nicht überlastet. Außerdem gibt es einen speziellen Höllenkreis, der Menschen vorbehalten ist, die in JS ihre eigenen Systeme für kaputte Objekte erfinden, anstatt auf dem bereits vorhandenen aufzubauen.
Millimoose
16

Es tut mir leid, eine alte Frage wiederzubeleben, aber ich dachte, ich könnte ein paar sehr grundlegende Beispiele und Erklärungen für Dummies beisteuern. Keine der anderen bisher veröffentlichten Antworten veranschaulicht die Syntax wie der MDN-Leitfaden erste Beispiel , ist.

Getter:

var settings = {
    firstname: 'John',
    lastname: 'Smith',
    get fullname() { return this.firstname + ' ' + this.lastname; }
};

console.log(settings.fullname);

... wird sich John Smithnatürlich anmelden . Ein Getter verhält sich wie eine variable Objekteigenschaft, bietet jedoch die Flexibilität einer Funktion, um ihren zurückgegebenen Wert im laufenden Betrieb zu berechnen. Es ist im Grunde eine ausgefallene Möglichkeit, eine Funktion zu erstellen, die beim Aufrufen nicht () erfordert.

Setter:

var address = {
    set raw(what) {
        var loc = what.split(/\s*;\s*/),
        area = loc[1].split(/,?\s+(\w{2})\s+(?=\d{5})/);

        this.street = loc[0];
        this.city = area[0];
        this.state = area[1];
        this.zip = area[2];
    }
};

address.raw = '123 Lexington Ave; New York NY  10001';
console.log(address.city);

... wird sich New Yorkan der Konsole anmelden . Wie Getter, Setter werden mit derselben Syntax aufgerufen wie der Wert einer Objekteigenschaft, sind jedoch eine weitere ausgefallene Möglichkeit, eine Funktion ohne () aufzurufen.

Siehe diese jsfiddle ein gründlicheres, vielleicht praktischeres Beispiel. Das Übergeben von Werten an den Objekt- Setter löst die Erstellung oder Auffüllung anderer Objektelemente aus. Insbesondere im jsfiddle-Beispiel fordert das Übergeben eines Arrays von Zahlen den Setter auf, Mittelwert, Median, Modus und Bereich zu berechnen. Legt dann die Objekteigenschaften für jedes Ergebnis fest.

Rojo
quelle
Ich verstehe immer noch nicht den Vorteil der Verwendung von get and set gegenüber der Erstellung einer getMethod oder setMethod. Ist der einzige Vorteil, den Sie ohne () aufrufen können? Es muss einen anderen Grund geben, warum es zu Javascript hinzugefügt wird.
Andreas
@Andreas Getter und Setter verhalten sich beim Aufrufen wie Eigenschaften, die helfen können, ihren beabsichtigten Zweck zu artikulieren. Sie schalten sonst fehlende Fähigkeiten nicht frei, aber ihre Verwendung kann Ihnen helfen, Ihre Gedanken zu organisieren. Das ist der wahre Vorteil. Als praktisches Beispiel habe ich einen Getter verwendet, um ein Google Maps-Objekt zu erweitern. Ich musste den Kamerarollwinkel berechnen, damit ich die Kartenkacheln flach zum Horizont drehen konnte. Google erledigt dies jetzt automatisch im Backend. aber zu der Zeit war es für mich hilfreich, maps.rollals Eigentum und nicht als maps.roll()Rückgabewert abzurufen. Es ist nur eine Präferenz.
Rojo
Es ist also nur syntaktischer Zucker, um Code ohne () sauberer aussehen zu lassen. Ich kann nicht verstehen, warum Sie Ihr Beispiel nicht mitmaps.roll()
Andreas
@Andreas Wer sagt, ich könnte nicht? Wie ich schon sagte, es ist nur eine Möglichkeit, meine Gedanken zu organisieren. Codierung ist eine Kunst. Sie fragen Bob Ross nicht, warum er gebrannten Ocker verwenden musste, wenn er Orange hätte verwenden können. Möglicherweise sehen Sie jetzt keinen Bedarf, aber eines Tages, wenn Sie entscheiden, dass Ihr Gemälde ein wenig gebranntes Ocker benötigt, wird es auf Ihrer Palette sein.
Rojo
:) Eine Sache, die ich sehe, dass get und set Syntax tut, ist die automatische Ausführung, wenn es als Eigenschaft einer Eigenschaft verwendet wird.
Andreas
11

Getter und Setter sind wirklich nur dann sinnvoll, wenn Sie private Eigenschaften von Klassen haben. Da Javascript nicht wirklich private Klasseneigenschaften hat, wie Sie es normalerweise von objektorientierten Sprachen erwarten, kann es schwer zu verstehen sein. Hier ist ein Beispiel für ein privates Zählerobjekt. Das Schöne an diesem Objekt ist, dass von außerhalb des Objekts nicht auf die interne Variable "count" zugegriffen werden kann.

var counter = function() {
    var count = 0;

    this.inc = function() {
        count++;
    };

    this.getCount = function() {
        return count;
    };
};

var i = new Counter();
i.inc();
i.inc();
// writes "2" to the document
document.write( i.getCount());

Wenn Sie immer noch verwirrt sind, lesen Sie den Artikel von Crockford über private Mitglieder in Javascript .

John
quelle
39
Ich bin nicht einverstanden. Getter und Setter sind auch sehr nützlich, um Informationen zu kapseln, deren Definition möglicherweise nicht nur eine einfache Variable ist. Dies kann nützlich sein, wenn Sie das Verhalten eines Systems ändern müssen, das zuvor einfache Eigenschaften verwendet hat und von dem andere Dinge abhängen können. Darüber hinaus zeigt Ihr Beispiel nur "Pseudo-Getter", die nur Funktionen sind. Echte JavaScript-Getter erscheinen als einfache Werte (und werden ohne Funktionsnotation aufgerufen), daher die wahre Leistung von ihnen.
Devios1
Ich bin mir nicht sicher, ob ich das als mächtig bezeichnen würde. Etwas, das als X erscheint, aber wirklich Y ist, ist nicht unbedingt klar. Ich würde absolut NICHT erwarten var baz = foo.bar, dass ein komplettes Set versteckter Verhaltensweisen dahinter steckt. Das würde ich allerdings erwarten foo.getBar().
AgmLauncher
8

Ich denke, der erste Artikel, auf den Sie verlinken, sagt es ziemlich deutlich aus:

Der offensichtliche Vorteil beim Schreiben von JavaScript auf diese Weise besteht darin, dass Sie obskure Werte verwenden können, auf die der Benutzer nicht direkt zugreifen soll.

Das Ziel hierbei ist es, die Felder zu kapseln und zu abstrahieren, indem nur über eine get()oder eine set()Methode auf sie zugegriffen wird. Auf diese Weise können Sie das Feld / die Daten intern beliebig speichern, externe Komponenten befinden sich jedoch nur außerhalb Ihrer veröffentlichten Schnittstelle. Auf diese Weise können Sie interne Änderungen vornehmen, ohne die externen Schnittstellen zu ändern, eine Validierung oder Fehlerprüfung innerhalb der set()Methode durchführen usw.

matt b
quelle
6

Obwohl wir es oft gewohnt sind, Objekte mit öffentlichen Eigenschaften ohne Zugriffskontrolle anzuzeigen, können wir mit JavaScript Eigenschaften genau beschreiben. Tatsächlich können wir Deskriptoren verwenden, um zu steuern, wie auf eine Eigenschaft zugegriffen werden kann und welche Logik wir auf sie anwenden können. Betrachten Sie das folgende Beispiel:

var employee = {
    first: "Boris",
    last: "Sergeev",
    get fullName() {
        return this.first + " " + this.last;
    },
    set fullName(value) {
        var parts = value.toString().split(" ");
        this.first = parts[0] || "";
        this.last = parts[1] || "";
    },
    email: "[email protected]"
};

Das Endergebnis:

console.log(employee.fullName); //Boris Sergeev
employee.fullName = "Alex Makarenko";

console.log(employee.first);//Alex
console.log(employee.last);//Makarenko
console.log(employee.fullName);//Alex Makarenko
Michael Horojanski
quelle
2

Was daran so verwirrend ist ... Getter sind Funktionen, die aufgerufen werden, wenn Sie eine Eigenschaft erhalten, Setter, wenn Sie sie festlegen. Beispiel, wenn Sie dies tun

obj.prop = "abc";

Sie setzen die Eigenschaft prop, wenn Sie Getter / Setter verwenden, wird die Setter-Funktion mit "abc" als Argument aufgerufen. Die Definition der Setterfunktion innerhalb des Objekts würde im Idealfall ungefähr so ​​aussehen:

set prop(var) {
   // do stuff with var...
}

Ich bin mir nicht sicher, wie gut das in allen Browsern implementiert ist. Es scheint, dass Firefox auch eine alternative Syntax hat, mit doppelt unterstrichenen speziellen ("magischen") Methoden. Wie üblich unterstützt Internet Explorer dies nicht.

Rolf
quelle
2

Sie können die Instanzmethode für die Klasse js über den Prototyp des Konstruktors definieren.

Es folgt der Beispielcode:

// BaseClass

var BaseClass = function(name) {
    // instance property
    this.name = name;
};

// instance method
BaseClass.prototype.getName = function() {
    return this.name;
};
BaseClass.prototype.setName = function(name) {
    return this.name = name;
};


// test - start
function test() {
    var b1 = new BaseClass("b1");
    var b2 = new BaseClass("b2");
    console.log(b1.getName());
    console.log(b2.getName());

    b1.setName("b1_new");
    console.log(b1.getName());
    console.log(b2.getName());
}

test();
// test - end

Und dies sollte für jeden Browser funktionieren. Sie können diesen Code auch einfach mit nodejs ausführen.

Eric Wang
quelle
1
Hiermit werden lediglich die neuen Methoden getName und setName erstellt. Diese beziehen sich nicht auf das Erstellen von Eigentum!
uzay95
2

Ich war auch etwas verwirrt über die Erklärung, die ich gelesen habe , weil ich versucht habe, einem vorhandenen Prototyp, den ich nicht geschrieben habe, eine Eigenschaft hinzuzufügen, sodass das Ersetzen des Prototyps als falscher Ansatz erschien. Also, für die Nachwelt, hier ist , wie ich hinzugefügt lastEigenschaft auf Array:

Object.defineProperty(Array.prototype, "last", {
    get: function() { return this[this.length - 1] }
});

Etwas schöner als das Hinzufügen einer Funktion IMHO.

shawkinaw
quelle
1

Wenn Sie sich auf das Konzept der Accessoren beziehen, besteht das einfache Ziel darin, den zugrunde liegenden Speicher vor willkürlichen Manipulationen zu verbergen. Der extremste Mechanismus dafür ist

function Foo(someValue) {
    this.getValue = function() { return someValue; }
    return this;
}

var myFoo = new Foo(5);
/* We can read someValue through getValue(), but there is no mechanism
 * to modify it -- hurrah, we have achieved encapsulation!
 */
myFoo.getValue();

Wenn Sie sich auf die eigentliche JS-Getter / Setter-Funktion beziehen, z. defineGetter/ defineSetter, oder { get Foo() { /* code */ } }, dann ist es erwähnenswert, dass in den meisten modernen Motoren die spätere Nutzung dieser Eigenschaften viel langsamer ist als sonst. z.B. Vergleichen Sie die Leistung von

var a = { getValue: function(){ return 5; }; }
for (var i = 0; i < 100000; i++)
    a.getValue();

vs.

var a = { get value(){ return 5; }; }
for (var i = 0; i < 100000; i++)
    a.value;
olliej
quelle
-1

Ich habe eine für euch, die vielleicht etwas hässlich ist, aber plattformübergreifend erledigt wird

function myFunc () {

var _myAttribute = "default";

this.myAttribute = function() {
    if (arguments.length > 0) _myAttribute = arguments[0];
    return _myAttribute;
}
}

auf diese Weise, wenn Sie anrufen

var test = new myFunc();
test.myAttribute(); //-> "default"
test.myAttribute("ok"); //-> "ok"
test.myAttribute(); //-> "ok"

Wenn Sie die Dinge wirklich aufpeppen möchten, können Sie eine Art Scheck einfügen:

if (arguments.length > 0 && typeof arguments[0] == "boolean") _myAttribute = arguments[0];
if (arguments.length > 0 && typeof arguments[0] == "number") _myAttribute = arguments[0];
if (arguments.length > 0 && typeof arguments[0] == "string") _myAttribute = arguments[0];

Oder gehen Sie noch verrückter mit dem erweiterten Typ-of- Check: type.of () -Code auf codingforums.com

artsy.ca
quelle
Es ging darum, ein Attribut in etwas ausgefalleneres ändern zu können, ohne die öffentliche Schnittstelle ändern zu müssen. Durch Hinzufügen eines call () -Tags wird es geändert.
Michael Scott Cuthbert