Getters \ setter per manichini


133

Ho cercato di mettere la mia testa intorno a getter e setter e non sta sprofondando. Ho letto JavaScript Getter e setter e Definire getter e setter e semplicemente non capisco .

Qualcuno può dichiarare chiaramente:

  1. Che cosa devono fare un getter e un setter, e
  2. Fai alcuni esempi MOLTO semplici?

12
Personalmente, non so come ottenere una spiegazione più chiara di quella di John ...
Jason Bunting,

Nel mondo di OO, getter e setter aiutano a scrivere una classe che è incapsulata correttamente
scambio eccessivo

Fondamentalmente la vedo così: stai definendo sovraccarichi per ottenere e impostare la proprietà, e questi sovraccarichi sono funzioni; ma non devi chiamarli. In questo modo è possibile sostituire a = setValue(5);con a = 5;e setValue()verrebbe quindi chiamato sotto il cofano per fare quello che ti piace.
Andrew,

Risposte:


102

Oltre alla risposta di @ millimoose , i setter possono anche essere usati per aggiornare altri valori.

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];
    }
};

Ora puoi impostare fullNamee firste lastverrà aggiornato e viceversa.

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"

2
@Akash: No, sebbene Internet Explorer 9 supporti la nuova Object.definePropertyfunzione in grado di definire getter e setter.
Matthew Crumley,

9
Non è davvero doloroso che MS non supporti correttamente JS e non faccia funzionare il loro Silverlight ovunque, quindi devo programmare tutto due volte, uno per SL e uno per il resto del mondo :)
Akash Kava

2
@Martin: potresti renderli privati ​​usando la stessa tecnica della risposta di John . Se si desidera utilizzare getter / setter reali, è necessario utilizzare this.__defineGetter__la Object.definePropertyfunzione più recente .
Matthew Crumley,

1
Solo un problema con l'approccio sopra elencato, se si desidera aggiungere getter e setter per la classe già esistente, sovrascriverà i prototipi e i metodi originali non saranno accessibili.
xchg.ca,

1
Questo approccio non sovrascrive Name.prototype.constructor? Sembra una cattiva alternativa alla risposta di millimoose .
r0estir0bbe,

62

Getter e setter in JavaScript

Panoramica

Getter e setter in JavaScript vengono utilizzati per definire proprietà calcolate o accessori . Una proprietà calcolata è quella che utilizza una funzione per ottenere o impostare un valore di oggetto. La teoria di base sta facendo qualcosa del genere:

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'

Ciò è utile per fare automaticamente cose dietro le quinte quando si accede a una proprietà, come mantenere i numeri nell'intervallo, riformattare stringhe, innescare eventi con valori modificati, aggiornare dati relazionali, fornire accesso a proprietà private e altro ancora.

Gli esempi seguenti mostrano la sintassi di base, anche se ottengono e impostano semplicemente il valore dell'oggetto interno senza fare nulla di speciale. Nei casi del mondo reale, modificare il valore di input e / o output in base alle proprie esigenze, come indicato sopra.

ottenere / impostare parole chiave

Supporti gete setparole chiave ECMAScript 5 per la definizione delle proprietà calcolate. Funzionano con tutti i browser moderni tranne IE 8 e versioni precedenti.

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

Getter e setter personalizzati

gete setnon sono parole riservate, quindi possono essere sovraccaricate per creare le tue funzioni di proprietà personalizzate, calcolate su più browser. Funzionerà con qualsiasi 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' );

O per un approccio più compatto, è possibile utilizzare una singola funzione.

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

Evita di fare qualcosa del genere, che può portare a un aumento del codice.

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

Per gli esempi precedenti, i nomi delle proprietà interne sono astratti con un trattino basso per scoraggiare gli utenti dal fare semplicemente foo.barvs. foo.get( 'bar' )e ottenere un valore "crudo". È possibile utilizzare il codice condizionale per eseguire operazioni diverse in base al nome della proprietà a cui si accede (tramite il nameparametro).

Object.defineProperty ()

L'utilizzo Object.defineProperty()è un altro modo per aggiungere getter e setter e può essere utilizzato sugli oggetti dopo averli definiti. Può anche essere usato per impostare comportamenti configurabili ed enumerabili. Questa sintassi funziona anche con IE 8, ma sfortunatamente solo su oggetti DOM.

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 __ ()

Infine, __defineGetter__()è un'altra opzione. È obsoleto, ma è ancora ampiamente utilizzato in tutto il Web e quindi è improbabile che scompaia presto. Funziona su tutti i browser tranne IE 10 e versioni precedenti. Anche se le altre opzioni funzionano bene anche su non IE, quindi questa non è così utile.

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

Vale anche la pena notare che negli ultimi esempi, i nomi interni devono essere diversi dai nomi degli accessi per evitare la ricorsione (ad esempio, foo.barchiamata foo.get(bar)chiamata foo.barchiamata chiamata foo.get(bar)...).

Guarda anche

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


1
In un approccio più compatto , this[ '_' + name ] = value;potrebbe esserci this[ '_' + name ] = arguments[1];e non sarebbe necessario specificare valuearg.
Redhart,

1
L'esempio: var foo = { bar : 123, get bar(){ return bar; }, set bar( value ){ this.bar = value; } }; foo.bar = 456; solleva un'eccezione: RangeError non rilevato: dimensione massima dello stack di chiamate superata nella barra Object.set [come barra] (<anonimo>: 4: 32) nella barra Object.set [come barra] (<anonimo>: 4: 32 ) nella barra Object.set [come barra] (<anonimo>: 4: 32) nella barra Object.set [come barra] (<anonimo>: 4: 32) nella barra Object.set [come barra] (<anonimo> : 4: 32) nella barra Object.set [as bar] (<anonimo>: 4: 32)
nevf

1
Il nome set / get deve essere diverso dal nome della proprietà. Quindi, invece di bar: 123e this.bar = valueecc., Cambiarli in _bar ad esempio. Vedi: hongkiat.com/blog/getters-setters-javascript
nevf

@nevf Grazie per la correzione! Sì, in genere con proprietà calcolate quella interna "reale" è denominata come _fooo mFoo. Se è uguale al getter / setter, causerà un loop infinito a causa della ricorsione e quindi uno Stack Overflow ™ ;-) perché quando dici a = b, chiama a.get (b) che a sua volta chiama a = b , che chiama a.get (b), ...
Beejor,

58

Li useresti ad esempio per implementare le proprietà calcolate.

Per esempio:

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)


Ok, penso di iniziare a prenderlo. Sto cercando di assegnare un getter alla proprietà length di un oggetto array ma ricevo un errore: "Redeclaration of var length" E il codice è simile al seguente: obj = []; obj .__ defineGetter __ ('length', function () {return this.length;});

1
Questo perché gli oggetti array hanno già una proprietà di lunghezza incorporata. Se la redeclaration fosse consentita, la chiamata della nuova lunghezza avrebbe un valore infinito. Prova a chiamare la proprietà "my_length" o alcuni di questi.
millimoose,

Per definire entrambi i getter in un'unica istruzione, utilizzare Object.defineProperties.
r0estir0bbe

Non puoi semplicemente creare {"area": ​​function () {return ...}}? semplicemente assegnarlo come proprietà dell'oggetto
RegarBoy

@developer Non è un getter Javascript come definito dalla lingua, è solo una funzione. Devi chiamarlo per ottenere il valore, non sovraccarica l'accesso alla proprietà. Inoltre c'è una cerchia speciale dell'inferno riservata alle persone che inventano i propri sistemi a oggetti rotti in JS invece di basarsi su quello che già possiede.
millimoose

16

Mi dispiace far risorgere una vecchia domanda, ma ho pensato di poter contribuire con un paio di esempi di base e spiegazioni per i manichini. Nessuna delle altre risposte pubblicate illustra così la sintassi come il primo esempio della guida MDN , che è il più semplice possibile.

getter:

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

console.log(settings.fullname);

... registrerà John Smith, ovviamente. Un getter si comporta come una proprietà di oggetto variabile, ma offre la flessibilità di una funzione per calcolare il valore restituito al volo. È fondamentalmente un modo elegante per creare una funzione che non richiede () quando si chiama.

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

... accederà New Yorkalla console. Come i getter, i setter vengono chiamati con la stessa sintassi dell'impostazione del valore di una proprietà dell'oggetto, ma sono ancora un altro modo fantasioso per chiamare una funzione senza ().

Vedi questo jsfiddle per un esempio più approfondito, forse più pratico. Il passaggio di valori nel setter dell'oggetto attiva la creazione o il popolamento di altri oggetti oggetto. Nello specifico, nell'esempio jsfiddle, il passaggio di una matrice di numeri richiede al setter di calcolare media, mediana, modalità e intervallo; quindi imposta le proprietà dell'oggetto per ciascun risultato.


Non capisco ancora i vantaggi dell'utilizzo di get e set rispetto alla creazione di getMethod o setMethod. L'unico vantaggio che puoi chiamarlo senza ()? Ci deve essere un altro motivo per cui è stato aggiunto a JavaScript.
Andreas,

@Andreas Getter e setter si comportano come proprietà quando vengono chiamati, il che può aiutare ad articolare lo scopo previsto. Non sbloccano abilità altrimenti mancanti, ma il loro uso può aiutarti a organizzare i tuoi pensieri. Questo è il vero vantaggio. Come esempio pratico, utilizzavo un getter per estendere un oggetto di Google Maps. Avevo bisogno di calcolare l'angolo di rotazione della fotocamera in modo da poter ruotare le tessere della mappa sull'orizzonte. Google lo fa automaticamente sul back-end ora; ma a quel tempo mi è stato utile recuperare maps.rollcome proprietà anziché maps.roll()restituire val. È solo una preferenza.
rojo,

quindi è solo zucchero sintattico a rendere il codice più pulito senza il (). Non riesco a capire perché non puoi avere il tuo esempio conmaps.roll()
Andreas,

@Andreas Chi dice che non potrei? Come ho detto, è solo un modo per aiutarmi a mantenere organizzati i miei pensieri. La codifica è un'arte. Non chiedi a Bob Ross perché ha dovuto usare l'ocra bruciata quando avrebbe potuto usare l'arancia. Potresti non vedere un bisogno ora, ma un giorno, quando deciderai che il tuo dipinto ha bisogno di un po 'di ocra bruciata, sarà sulla tua tavolozza.
rojo,

:) Una cosa che vedo che ottiene e imposta la sintassi fa, è essere eseguito automaticamente se viene utilizzato come proprietà di una proprietà.
Andreas,

11

Getter e setter hanno davvero senso solo quando hai proprietà private delle classi. Dal momento che Javascript non ha proprietà di classe private come normalmente si pensa dai linguaggi orientati agli oggetti, può essere difficile da capire. Ecco un esempio di un oggetto contatore privato. La cosa bella di questo oggetto è che la variabile interna "count" non è accessibile dall'esterno dell'oggetto.

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

Se sei ancora confuso, dai un'occhiata all'articolo di Crockford sui membri privati ​​in Javascript .


39
Non sono d'accordo. Getter e setter sono anche molto utili per incapsulare informazioni la cui definizione potrebbe non essere solo una semplice variabile. Può essere utile se è necessario modificare il comportamento di un sistema che in precedenza utilizzava proprietà semplici e da quali altre cose potrebbero dipendere. Inoltre, il tuo esempio dimostra solo "pseudo-getter" che sono solo funzioni. I getter JavaScript reali vengono visualizzati come valori semplici (e sono accessibili senza notazione di funzione), da cui il vero potere di essi.
devios1

Non sono sicuro che lo definirei così potente. Qualcosa che appare come X ma in realtà Y non è necessariamente chiaro. Non mi aspetterei assolutamente var baz = foo.bardi avere una serie completa di comportamenti nascosti dietro di esso. Me lo aspetterei foo.getBar(), comunque.
AgmLauncher,

8

Penso che il primo articolo a cui ti colleghi lo afferma abbastanza chiaramente:

L'ovvio vantaggio di scrivere JavaScript in questo modo è che puoi usarlo oscurando i valori a cui non vuoi che l'utente acceda direttamente.

L'obiettivo qui è incapsulare e sottrarre i campi consentendo l'accesso solo attraverso un metodo get()o set(). In questo modo, è possibile archiviare i campi / i dati internamente nel modo desiderato, ma i componenti esterni sono solo lontani dall'interfaccia pubblicata. Ciò consente di apportare modifiche interne senza modificare le interfacce esterne, di eseguire alcune convalide o il controllo degli errori all'interno del set()metodo, ecc.


6

Sebbene spesso siamo abituati a vedere oggetti con proprietà pubbliche senza alcun controllo di accesso, JavaScript ci consente di descrivere accuratamente le proprietà. In effetti, possiamo usare descrittori per controllare come è possibile accedere a una proprietà e quale logica possiamo applicare ad essa. Considera il seguente esempio:

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: "boris.sergeev@example.com"
};

Il risultato finale:

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

2

Cosa c'è di così confuso al riguardo ... i getter sono funzioni che vengono chiamate quando si ottiene una proprietà, setter, quando la si imposta. esempio, se lo fai

obj.prop = "abc";

Stai impostando la proprietà prop, se stai usando getter / setter, allora la funzione setter verrà chiamata, con "abc" come argomento. La definizione della funzione setter all'interno dell'oggetto sarebbe idealmente simile a questa:

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

Non sono sicuro di quanto bene sia implementato nei browser. Sembra che Firefox abbia anche una sintassi alternativa, con metodi speciali ("magici") a doppia sottolineatura. Come al solito, Internet Explorer non supporta nulla di tutto ciò.


2

È possibile definire il metodo di istanza per la classe js, tramite il prototipo del costruttore.

Di seguito è riportato il codice di esempio:

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

E, questo dovrebbe funzionare per qualsiasi browser, puoi anche semplicemente usare nodejs per eseguire questo codice.


1
Questo sta solo creando nuovi metodi getName e setName. Questi non sono legati alla creazione di proprietà!
uzay95,

2

Sono stato anche un po 'confuso dalla spiegazione che ho letto , perché stavo cercando di aggiungere una proprietà a un prototipo esistente che non avevo scritto, quindi sostituire il prototipo sembrava un approccio sbagliato. Quindi, per i posteri, ecco come ho aggiunto una lastproprietà a Array:

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

Molto leggermente più bello dell'aggiunta di una funzione IMHO.


1

Se ti riferisci al concetto di accessori, il semplice obiettivo è nascondere la memoria sottostante da manipolazioni arbitrarie. Il meccanismo più estremo per questo è

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

Se ti riferisci all'effettiva funzione getter / setter JS, ad es. defineGetter/ defineSetter, oppure { get Foo() { /* code */ } }, vale la pena notare che nella maggior parte dei motori moderni il successivo utilizzo di tali proprietà sarà molto più lento di quanto non sarebbe altrimenti. per esempio. confrontare le prestazioni di

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;

-1

Ne ho uno per voi ragazzi che potrebbe essere un po 'brutto, ma lo fa su piattaforme diverse

function myFunc () {

var _myAttribute = "default";

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

in questo modo, quando chiami

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

Se vuoi davvero rendere le cose più piccanti .. puoi inserire un tipo di controllo:

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];

oppure diventa ancora più folle con il controllo typeof avanzato: type.of () su codingforums.com


il punto era poter cambiare un attributo in qualcosa di più elaborato senza dover cambiare l'interfaccia pubblica. L'aggiunta di un tag call () lo modifica.
Michael Scott Cuthbert,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.