Gettery \ setery dla manekinów

133

Próbowałem poradzić sobie z metodami pobierającymi i ustawiającymi, ale to nie zagłębia się. Przeczytałem JavaScript Getters and Setters i Defining Getters and Setters i po prostu nie rozumiem .

Czy ktoś może jasno stwierdzić:

  1. Co getter i setter mają zrobić i
  2. Podaj BARDZO proste przykłady?
Alexander Abakumov
źródło
12
Osobiście nie wiem, jak uzyskać jaśniejsze wyjaśnienie niż John's ...
Jason Bunting
W świecie OO getter i setter pomagają napisać klasę, która jest odpowiednio hermetyzowana
nadmierna wymiana
Zasadniczo widzę to w ten sposób: definiujesz przeciążenia do pobierania i ustawiania właściwości, a te przeciążenia są funkcjami; ale nie musisz do nich dzwonić. W ten sposób możesz zastąpić a = setValue(5);go, a = 5;a setValue()tym samym zostać wezwanym pod maskę, aby robić, co chcesz.
Andrew,

Odpowiedzi:

102

Oprócz odpowiedzi @ millimoose, metody ustawiające mogą być również używane do aktualizowania innych wartości.

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

Teraz można ustawić fullName, a firsti lastbędzie aktualizowany i vice versa.

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
źródło
2
@Akash: Nie, chociaż Internet Explorer 9 obsługuje nowszą Object.definePropertyfunkcję, która może definiować metody pobierające i ustawiające.
Matthew Crumley,
9
Czy to naprawdę nie bolesne, że MS nie obsługuje poprawnie JS i nie powodują, że ich silverlight działa wszędzie, więc muszę programować wszystko dwa razy, jeden dla SL, a drugi dla reszty świata :)
Akash Kava
2
@Martin: Możesz uczynić je prywatnymi, używając tej samej techniki, co w odpowiedzi Johna . Jeśli chcesz używać prawdziwych metod pobierających / ustawiających, musisz użyć this.__defineGetter__lub nowszej Object.definePropertyfunkcji.
Matthew Crumley,
1
Tylko jeden problem z podejściem wymienionym powyżej, jeśli chcesz dodać metody pobierające i ustawiające dla już istniejącej klasy, nadpisuje prototypy, a oryginalne metody nie będą dostępne.
xchg.ca
1
Czy to podejście nie nadpisuje Name.prototype.constructor? Wydaje się, że to zła alternatywa dla odpowiedzi millimoose'a .
r0estir0bbe
62

Metody pobierające i ustawiające w JavaScript

Przegląd

Pobierające i ustawiające w JavaScript są wykorzystywane do definiowania obliczonych właściwości lub akcesorów . Właściwość obliczona to taka, która używa funkcji do pobrania lub ustawienia wartości obiektu. Podstawowa teoria robi coś takiego:

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'

Jest to przydatne do automatycznego wykonywania czynności za kulisami, gdy uzyskuje się dostęp do właściwości, takich jak utrzymywanie liczb w zakresie, ponowne formatowanie łańcuchów, wyzwalanie zdarzeń spowodowanych zmianą wartości, aktualizowanie danych relacyjnych, zapewnianie dostępu do właściwości prywatnych i nie tylko.

Poniższe przykłady pokazują podstawową składnię, chociaż po prostu pobierają i ustawiają wewnętrzną wartość obiektu, nie robiąc nic specjalnego. W rzeczywistych przypadkach zmodyfikowałbyś wartość wejściową i / lub wyjściową, aby dopasować ją do swoich potrzeb, jak wspomniano powyżej.

pobierz / ustaw słowa kluczowe

ECMAScript 5 obsługuje get i setsłowa kluczowe do definiowania obliczonych właściwości. Działają ze wszystkimi nowoczesnymi przeglądarkami z wyjątkiem IE 8 i starszych.

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

Niestandardowe metody pobierające i ustawiające

get i set nie są słowami zastrzeżonymi, więc można je przeciążać w celu tworzenia własnych niestandardowych funkcji obliczanych właściwości w różnych przeglądarkach. To zadziała w każdej przeglądarce.

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

Lub dla bardziej zwartego podejścia, można użyć pojedynczej funkcji.

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

Unikaj robienia czegoś takiego, co może prowadzić do rozdęcia kodu.

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

W powyższych przykładach nazwy właściwości wewnętrznych są wyabstrahowane podkreśleniem, aby zniechęcić użytkowników do prostego działania foo.bar vs. foo.get( 'bar' )i uzyskiwania wartości „niegotowanej”. Możesz użyć kodu warunkowego, aby wykonać różne czynności w zależności od nazwy właściwości, do której uzyskujesz dostęp (za pośrednictwem nameparametru).

Object.defineProperty ()

Użycie Object.defineProperty()jest innym sposobem dodawania metod pobierających i ustawiających i może być używane na obiektach po ich zdefiniowaniu. Może być również używany do ustawiania konfigurowalnych i wyliczalnych zachowań. Ta składnia działa również z IE 8, ale niestety tylko w przypadku obiektów 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 __ ()

Wreszcie __defineGetter__()jest inna opcja. Jest przestarzały, ale nadal jest szeroko stosowany w sieci, więc prawdopodobnie nie zniknie w najbliższym czasie. Działa na wszystkich przeglądarkach oprócz IE 10 i starszych. Chociaż inne opcje działają również dobrze w wersji innej niż IE, więc ta nie jest tak przydatna.

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

Warto również zauważyć, że w tych ostatnich przykładach nazwy wewnętrzne muszą być inne niż nazwy akcesorów, aby uniknąć rekursji (tj. foo.barWywoływanie foo.get(bar)wywoływania foo.barwywoływania foo.get(bar)...).

Zobacz też

MDN get , set , Object.defineProperty () , __defineGetter __ () , __defineSetter __ () Obsługa
MSDN IE8 Getter

Beejor
źródło
1
W bardziej zwartej podejście , this[ '_' + name ] = value;może być this[ '_' + name ] = arguments[1];i nie będzie potrzeby, aby określić valuearg.
Redhart
1
Przykład: var foo = { bar : 123, get bar(){ return bar; }, set bar( value ){ this.bar = value; } }; foo.bar = 456; Zgłasza wyjątek: Uncaught RangeError: Przekroczono maksymalny rozmiar stosu wywołań na pasku Object.set [as bar] (<anonymous>: 4: 32) at Object.set bar [as bar] (<anonymous>: 4: 32 ) w Object.set bar [as bar] (<anonymous>: 4: 32) at Object.set bar [as bar] (<anonymous>: 4: 32) at Object.set bar [as bar] (<anonymous> : 4:32) na pasku Object.set [as bar] (<anonymous>:
4:32
1
Nazwa zestawu / pobrania musi być inna niż nazwa właściwości. Więc zamiast bar: 123i this.bar = valueitp. Zmień je _bar na przykład. Zobacz: hongkiat.com/blog/getters-setters-javascript
nevf
@nevf Dzięki za korektę! Tak, zazwyczaj w przypadku obliczonych właściwości „prawdziwy” wewnętrzny ma nazwę _foolub mFoo. Jeśli jest to to samo co getter / setter, spowoduje to nieskończoną pętlę z powodu rekursji, a następnie Stack Overflow ™ ;-), ponieważ kiedy powiesz a = b, wywoła a.get (b), które samo wywołuje a = b , która wywołuje a.get (b), ...
Beejor
58

Używałbyś ich na przykład do implementacji obliczonych właściwości.

Na przykład:

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
źródło
Ok, myślę, że zaczynam to rozumieć. Próbuję przypisać funkcję pobierającą do właściwości length obiektu tablicy, ale pojawia się błąd: „Ponowna deklaracja zmiennej długości” A kod wygląda następująco: obj = []; obj .__ defineGetter __ ('length', function () {return this.length;});
1
Dzieje się tak, ponieważ obiekty Array mają już wbudowaną właściwość length. Jeśli ponowna deklaracja byłaby dozwolona, ​​wywołanie nowej długości powtarzałoby się w nieskończoność. Spróbuj wywołać właściwość „my_length” lub coś podobnego.
millimoose
Aby zdefiniować oba metody pobierające w jednej instrukcji, użyj Object.defineProperties.
r0estir0bbe
Nie możesz po prostu zrobić {"area": ​​function () {return ...}}? po prostu przypisz go jako właściwość obiektu
RegarBoy
@developer To nie jest getter JavaScript w rozumieniu języka, to tylko funkcja. Musisz go wywołać, aby uzyskać wartość, nie przeciąża to dostępu do właściwości. Istnieje również specjalny krąg piekła zarezerwowany dla ludzi, którzy wymyślają własne zepsute systemy obiektów w JS, zamiast budować na tym, który już ma.
millimoose
16

Przepraszam, że wskrzeszam stare pytanie, ale pomyślałem, że mogę wnieść kilka bardzo podstawowych przykładów i wyjaśnień dla manekinów. Żadna z pozostałych odpowiedzi opublikowanych w ten sposób nie ilustruje składni tak jak pierwszy przykład z przewodnika MDN , który jest tak prosty, jak tylko można.

Rębacz:

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

console.log(settings.fullname);

... będzie się logować John Smith oczywiście się . Funkcja pobierająca zachowuje się jak zmienna właściwość obiektu, ale zapewnia elastyczność funkcji do obliczania zwracanej wartości w locie. Jest to po prostu fantazyjny sposób tworzenia funkcji, która nie wymaga () podczas wywoływania.

Seter:

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

... będzie się logować New York się do konsoli. Podobnie jak metody pobierające, metody ustawiające są wywoływane z taką samą składnią, jak ustawianie wartości właściwości obiektu, ale są kolejnym wymyślnym sposobem wywołania funkcji bez ().

Zobacz ten plik jsfiddle, aby uzyskać dokładniejszy, być może bardziej praktyczny przykład. Przekazywanie wartości do metody ustawiającej obiekt wyzwala tworzenie lub zapełnianie innych elementów obiektu. W szczególności, w przykładzie z jsfiddle, przekazanie tablicy liczb skłania ustawiającego do obliczenia średniej, mediany, trybu i zakresu; następnie ustawia właściwości obiektu dla każdego wyniku.

rojo
źródło
Nadal nie rozumiem korzyści z używania get and set w porównaniu z tworzeniem getMethod lub setMethod. Czy jedyna korzyść, jaką możesz nazwać bez ()? Musi być inny powód, dla którego został dodany do javascript.
Andreas
@Andreas Programy pobierające i ustawiające zachowują się jak właściwości po wywołaniu, co może pomóc w określeniu ich zamierzonego celu. Nie odblokowują brakujących zdolności, ale ich użycie może pomóc Ci uporządkować myśli. To prawdziwa korzyść. Jako praktyczny przykład użyłem metody pobierającej do rozszerzenia obiektu Google Maps. Musiałem obliczyć kąt przechyłu kamery, aby móc obracać kafelki mapy płasko do horyzontu. Google robi to teraz automatycznie na zapleczu; ale w tamtym czasie pomocne było dla mnie pobranie wartości maps.rolljako właściwości, a nie wartości maps.roll()zwrotu. To tylko preferencja.
rojo
więc to po prostu cukier składniowy, aby kod wyglądał bardziej czysto bez (). Nie rozumiem, dlaczego nie mogłeś dać przykładu zmaps.roll()
Andreasem
@Andreas Kto powiedział, że nie mogłem? Jak mówię, to tylko sposób, aby pomóc mi uporządkować myśli. Kodowanie to sztuka. Nie pytasz Boba Rossa, dlaczego musiał używać spalonej ochry, skoro mógł użyć pomarańczy. Być może teraz nie widzisz takiej potrzeby, ale pewnego dnia, kiedy zdecydujesz, że Twój obraz potrzebuje trochę spalonej ochry, będzie na Twojej palecie.
rojo
:) Jedyną rzeczą, którą widzę, że robi to składnia get and set, jest automatyczne uruchamianie, jeśli jest używane jako właściwość właściwości.
Andreas
11

Metody pobierające i ustawiające mają sens tylko wtedy, gdy masz prywatne właściwości klas. Ponieważ Javascript tak naprawdę nie ma właściwości klasy prywatnej, jak normalnie myślisz w językach obiektowych, może być trudny do zrozumienia. Oto jeden przykład prywatnego obiektu licznika. Zaletą tego obiektu jest to, że do zmiennej wewnętrznej „count” nie można uzyskać dostępu z zewnątrz obiektu.

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

Jeśli nadal jesteś zdezorientowany, zapoznaj się z artykułem Crockforda na temat prywatnych członków w Javascript .

Jan
źródło
39
Nie zgadzam się. Metody pobierające i ustawiające są również bardzo przydatne do enkapsulacji informacji, których definicja może nie być zwykłą zmienną. Może to być przydatne, jeśli musisz zmienić zachowanie systemu, który wcześniej używał prostych właściwości i od którego mogą zależeć inne rzeczy. Co więcej, twój przykład demonstruje tylko "pseudo pobierające", które są po prostu funkcjami. Prawdziwe metody pobierające JavaScript pojawiają się jako proste wartości (i są dostępne bez notacji funkcji), stąd ich prawdziwa moc.
devios1
Nie jestem pewien, czy nazwałbym to potężnym. Coś, co pojawia się jako X, ale naprawdę jest Y, niekoniecznie jest jasne. Absolutnie NIE spodziewałbym var baz = foo.barsię, że kryje się za tym pełny zestaw ukrytych zachowań. I byłoby oczekiwać, że od foo.getBar()krytyki.
AgmLauncher
8

Wydaje mi się, że pierwszy artykuł, do którego odsyłasz, stwierdza to dość jasno:

Oczywistą zaletą pisania JavaScript w ten sposób jest to, że można go używać do ukrywania wartości, do których użytkownik nie powinien mieć bezpośredniego dostępu.

Celem jest tutaj hermetyzacja i abstrakcja pól, umożliwiając dostęp do nich tylko za pomocą metody get()lub set(). W ten sposób możesz przechowywać pole / dane wewnętrznie w dowolny sposób, ale zewnętrzne komponenty są tylko z dala od opublikowanego interfejsu. Pozwala to na dokonywanie zmian wewnętrznych bez zmiany zewnętrznych interfejsów, dokonywanie walidacji lub sprawdzania błędów w set()metodzie itp.

matowe b
źródło
6

Chociaż często jesteśmy przyzwyczajeni do oglądania obiektów z właściwościami publicznymi bez żadnej kontroli dostępu, JavaScript pozwala nam dokładnie opisać właściwości. W rzeczywistości możemy użyć deskryptorów, aby kontrolować, w jaki sposób można uzyskać dostęp do właściwości i jaką logikę możemy do niej zastosować. Rozważmy następujący przykład:

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

Wynik końcowy:

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
źródło
2

Co w tym jest tak mylącego… metody pobierające to funkcje, które są wywoływane, gdy otrzymujesz właściwość, czyli ustawiacze, gdy ją ustawisz. na przykład, jeśli tak

obj.prop = "abc";

Ustawiasz właściwość właściwości, jeśli używasz metod pobierających / ustawiających, zostanie wywołana funkcja ustawiająca z „abc” jako argumentem. Definicja funkcji ustawiającej wewnątrz obiektu idealnie wyglądałaby mniej więcej tak:

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

Nie jestem pewien, jak dobrze jest to zaimplementowane w różnych przeglądarkach. Wygląda na to, że Firefox ma również alternatywną składnię, ze specjalnymi („magicznymi”) metodami z podwójnym podkreśleniem. Jak zwykle Internet Explorer nie obsługuje tego.

Rolf
źródło
2

Możesz zdefiniować metodę instancji dla klasy js, poprzez prototyp konstruktora.

Poniżej znajduje się przykładowy kod:

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

Powinno to działać dla każdej przeglądarki, możesz też po prostu użyć nodejs do uruchomienia tego kodu.

Eric Wang
źródło
1
To tylko tworzenie nowych metod getName i setName. Nie są one związane z tworzeniem nieruchomości!
uzay95
2

Byłem również nieco zdezorientowany wyjaśnieniem, które przeczytałem , ponieważ próbowałem dodać właściwość do istniejącego prototypu, którego nie napisałem, więc zastąpienie prototypu wydawało się złym podejściem. Tak więc, dla potomności, oto jak dodałem lastwłaściwość do Array:

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

Nieco ładniejsze niż dodanie funkcji IMHO.

shawkinaw
źródło
1

Jeśli odnosisz się do koncepcji akcesorów, prostym celem jest ukrycie podstawowej pamięci masowej przed dowolną manipulacją. Najbardziej ekstremalnym mechanizmem jest to

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

Jeśli odnosisz się do rzeczywistej funkcji getter / setter JS, np. defineGetter/ defineSetterlub { get Foo() { /* code */ } }, wtedy warto zauważyć, że w większości nowoczesnych silników późniejsze wykorzystanie tych właściwości będzie znacznie wolniejsze niż w innym przypadku. na przykład. porównaj wydajność

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
źródło
-1

Mam dla was jeden, który może być trochę brzydki, ale działa na różnych platformach

function myFunc () {

var _myAttribute = "default";

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

w ten sposób, kiedy dzwonisz

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

Jeśli naprawdę chcesz coś urozmaicić ... możesz wstawić rodzaj czeku:

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

lub jeszcze bardziej szalony dzięki zaawansowanemu kodowi typu check: type.of () na stronie codingforums.com

artsy.ca
źródło
chodziło o możliwość zmiany atrybutu na coś bardziej wyszukanego bez konieczności zmiany interfejsu publicznego. Dodanie tagu call () zmienia go.
Michael Scott Cuthbert