Aptallar için Getters \ setters


133

Ben Alıcı ve ayarlayıcıların ve çevresinde başım almaya çalışıyorum onun içinde batmıyor. Okudum JavaScript Alıcılar ve ayarlayıcılar ve tanımlanması Alıcılar ve ayarlayıcılar ve sadece almıyor.

Birisi açıkça şunu söyleyebilir mi?

  1. Alıcı ve ayarlayıcının ne anlama geldiği ve
  2. ÇOK basit örnekler verebilir misiniz?

12
Şahsen, John'unkinden nasıl daha net bir açıklama yapabileceğinizi bilmiyorum ...
Jason Bunting

OO dünyasında, alıcı ve ayarlayıcı, düzgün bir şekilde kapsüllenmiş bir sınıf yazmaya yardımcı olur
17'de

Temel olarak şöyle görüyorum: özelliği almak ve ayarlamak için aşırı yükleri tanımlıyorsunuz ve bu aşırı yükler işlevlerdir; ama, onları aramak zorunda değilsiniz. Değiştirmek Bu şekilde a = setValue(5);olan a = 5;ve setValue()dolayısıyla ne gibi yapmak kaputun altında olacağını söyledi.
Andrew

Yanıtlar:


102

@ Millimoose'un cevabına ek olarak , ayarlayıcılar diğer değerleri güncellemek için de kullanılabilir.

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

Şimdi, ayarlayabilirsiniz fullNameve firstve lasttersi güncellenir ve yardımcısı alınacaktır.

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: Hayır, ancak, Internet Explorer 9 Object.definePropertyalıcıları ve ayarlayıcıları tanımlayabilen daha yeni işlevi desteklemez .
Matthew Crumley

9
MS'nin JS'yi doğru bir şekilde desteklememesi ve gümüş ışığını her yerde çalıştırmamaları gerçekten acı verici değil mi, bu yüzden her şeyi iki kez programlamak zorundayım, biri SL için ve diğeri dünyanın geri kalanı için :)
Akash Kava

2
@Martin: John'un cevabı ile aynı tekniği kullanarak onları özel yapabilirsiniz . Gerçek alıcılar / ayarlayıcılar kullanmak istiyorsanız this.__defineGetter__veya daha yeni bir Object.definePropertyişlev kullanmanız gerekir .
Matthew Crumley

1
Yukarıda listelenen yaklaşımla ilgili yalnızca bir sorun, zaten var olan sınıf için alıcılar ve ayarlayıcılar eklemek istiyorsanız, prototipleri geçersiz kılar ve orijinal yöntemlere erişilemez.
xchg.ca

1
Bu yaklaşımın üzerine yazmıyor Name.prototype.constructormu? Millimoose cevabına kötü bir alternatif gibi görünüyor .
r0estir0bbe

62

JavaScript'teki Harfler ve Ayarlayıcılar

genel bakış

JavaScript'teki harfler ve ayarlayıcılar, hesaplanan özellikleri veya erişimcileri tanımlamak için kullanılır . Hesaplanan özellik, bir nesne değerini almak veya ayarlamak için bir işlev kullanan bir özelliktir. Temel teori böyle bir şey yapmaktır:

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'

Bu, sayıları aralıkta tutmak, dizeleri yeniden biçimlendirmek, değer değiştirilmiş olayları tetiklemek, ilişkisel verileri güncellemek, özel mülklere erişim sağlamak ve daha fazlası gibi bir özelliğe erişildiğinde perde arkasındaki işleri otomatik olarak yapmak için kullanışlıdır.

Aşağıdaki örnekler temel sözdizimini gösterir, ancak özel bir şey yapmadan dahili nesne değerini alır ve ayarlarlar. Gerçek dünyadaki durumlarda, girdi ve / veya çıktı değerini yukarıda belirtildiği gibi ihtiyaçlarınıza uyacak şekilde değiştirebilirsiniz.

get / set Anahtar kelimeler

ECMAScript 5 destekler getve setbilgisayarlı özelliklerini tanımlamak için anahtar kelimeler. IE 8 ve altı dışındaki tüm modern tarayıcılarla çalışırlar.

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

Özel Harfler ve Ayarlayıcılar

getve setayrılmış kelimeler olmadığından, kendi özel, tarayıcılar arası hesaplanmış özellik işlevlerinizi oluşturmak için aşırı yüklenebilirler. Bu herhangi bir tarayıcıda çalışır.

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

Ya da daha kompakt bir yaklaşım için, tek bir işlev kullanılabilir.

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

Kod blokajına yol açabilecek böyle bir şey yapmaktan kaçının.

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

Yukarıdaki örneklerde, dahili özellik adları, kullanıcıların basitçe foo.barvs. yapmalarını foo.get( 'bar' )ve "pişmemiş" bir değer elde etmelerini önlemek için alt çizgiyle özetlenmiştir . Erişilen özelliğin adına ( nameparametre üzerinden ) bağlı olarak farklı şeyler yapmak için koşullu kod kullanabilirsiniz .

Object.defineProperty ()

Kullanmak Object.defineProperty()alıcıları ve ayarlayıcıları eklemenin başka bir yoludur ve tanımlandıktan sonra nesnelerde kullanılabilir. Ayrıca yapılandırılabilir ve numaralandırılabilir davranışları ayarlamak için de kullanılabilir. Bu sözdizimi IE 8 ile de çalışır, ancak maalesef yalnızca DOM nesnelerinde.

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

Son olarak, __defineGetter__()başka bir seçenektir. Kullanımdan kaldırıldı, ancak yine de web'de yaygın olarak kullanılıyor ve bu nedenle yakında herhangi bir zamanda kaybolması pek mümkün değil. IE 10 ve altı dışındaki tüm tarayıcılarda çalışır. Her ne kadar diğer seçenekler IE olmayan da iyi çalışır rağmen, bu yüzden bu yararlı değil.

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

Ayrıca belirterek değer ikincisi örneklerde, iç isimler önlemek özyineleme erişimci adlarıyla (yani farklı olması gerektiğidir foo.barçağıran foo.get(bar)çağıran foo.barçağrı foo.get(bar)...).

Ayrıca bakınız

MDN get , set , Object.defineProperty () , __defineGetter __ () , __defineSetter __ ()
MSDN IE8 Getter Desteği


1
Gelen daha kompakt bir yaklaşım , this[ '_' + name ] = value;olabilir this[ '_' + name ] = arguments[1];ve belirtmek için gerek olmazdı valuearg.
Redhart

1
Örnek: var foo = { bar : 123, get bar(){ return bar; }, set bar( value ){ this.bar = value; } }; foo.bar = 456; Bir istisna oluşturur: Yakalanmamış RangeError: Object.set çubuğunda [bar] (<anonymous>: 4: 32) Object.set çubuğunda [bar] (<anonymous>: 4: 32) maksimum çağrı yığını boyutu aşıldı ) Object.set çubuğunda [çubuk olarak] (<anonymous>: 4: 32) Object.set çubuğunda [çubuk olarak] (<anonymous>: 4: 32) Object.set çubuğunda [çubuk olarak] (<anonymous> : 4: 32) Object.set bar'da [as bar] (<anonim>: 4: 32)
nevf

1
Set / get adı, mülk adından farklı olmalıdır. Yani yerine bar: 123ve this.bar = valuevb için bu değişiklik _bar , örneğin. Bakınız: hongkiat.com/blog/getters-setters-javascript
nevf

@nevf Düzeltme için teşekkürler! Evet, tipik olarak hesaplanan özelliklerle "gerçek" dahili olana _fooveya adı verilir mFoo. Alıcı / ayarlayıcı ile aynıysa, özyineleme ve ardından bir Stack Overflow ™ ;-) nedeniyle sonsuz bir döngüye neden olur, çünkü a = b dediğinizde, kendisi a = b olarak adlandırılan a.get (b) öğesini çağırır. , a.get (b) olarak adlandırılan, ...
Beejor

58

Bunları örneğin hesaplanan özellikleri uygulamak için kullanırsınız.

Örneğin:

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)


Tamam, sanırım anlamaya başlıyorum. Bir dizi nesnesinin length özelliğine bir getter atamaya çalışıyorum ama bir hata alıyorum: "Değişken uzunlukta redeclaration" Ve kod böyle görünüyor: obj = []; obj .__ defineGetter __ ('uzunluk', işlev () {dönüş this.length;});

1
Array nesneleri zaten yerleşik uzunluk özelliğine sahip olmasıdır. Yeniden açıklama yapılmasına izin verilirse, yeni uzunluğun çağrılması sonsuza dek geri çekilirdi. "My_length" mülkünü veya benzerlerini aramayı deneyin.
20'de millimoose

Her iki alıcıyı bir deyimde tanımlamak için kullanın Object.defineProperties.
r0estir0bbe

Sadece {"area": ​​function () {return ...}} yapamaz mısınız? sadece bir nesne özelliği olarak atayın
RegarBoy

@developer Bu dil tarafından tanımlanan bir Javascript alıcısı değil, sadece bir işlev. Değeri almak için çağırmanız gerekir, mülke erişimi aşırı yüklemez. Ayrıca, zaten mevcut olanı üzerine inşa etmek yerine JS'de kendi kırık nesne sistemlerini icat eden insanlar için ayrılmış özel bir cehennem çemberi var.
millimoose

16

Eski bir soruyu dirilttiğim için özür dilerim, ama birkaç temel örneğe ve aptalca açıklamaya katkıda bulunabileceğimi düşündüm. Şimdiye kadar gönderilen diğer cevapların hiçbiri MDN kılavuzunun ilk örneği gibi sözdizimini göstermemektedir .

Getter:

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

console.log(settings.fullname);

... John Smithtabii ki giriş yapacaktır . Bir alıcı değişken nesne özelliği gibi davranacağını ancak teklifler anında onun döndü değerini hesaplamak için bir fonksiyonun esnekliği. Temelde, arama yaparken () gerektirmeyen bir işlev oluşturmanın süslü bir yoludur.

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

... New Yorkkonsola giriş yapacak. Alıcılar gibi, ayarlayıcılar , bir nesne özelliğinin değerini ayarlamakla aynı sözdizimiyle çağrılır, ancak () olmadan bir işlevi çağırmanın başka bir fantezi yoludur.

Daha kapsamlı, belki daha pratik bir örnek için bu jsfiddle'a bakın . Değerleri nesnenin ayarlayıcısına geçirmek, diğer nesne öğelerinin oluşturulmasını veya popülasyonunu tetikler. Özellikle, jsfiddle örneğinde, bir sayı dizisinin iletilmesi ayarlayıcıdan ortalama, medyan, mod ve aralığı hesaplamasını ister; daha sonra her sonuç için nesne özelliklerini ayarlar.


Get ve set ile getMethod veya setMethod oluşturma avantajlarını hala anlamıyorum. () Olmadan çağırabileceğiniz tek fayda var mı? Javascript'e eklenmesi için başka bir neden olmalı.
Andreas

@Andreas Harfler ve ayarlayıcılar, çağrıldığında özellik gibi davranır ve bu da amaçlanan amaçlarını ifade etmeye yardımcı olabilir. Aksi halde eksik yeteneklerin kilidini açmazlar, ancak kullanımları düşüncelerinizi düzenlemenize yardımcı olabilir. Asıl fayda budur. Pratik bir örnek olarak, bir Google Haritalar nesnesini genişletmek için bir alıcı kullandım. Harita döşemelerini ufka doğru döndürmek için kamera rulo açısını hesaplamam gerekiyordu. Google bunu şimdi otomatik olarak arka uçta yapıyor; ama o zaman dönüş valinden maps.rollziyade bir mülk olarak almak bana yardımcı oldu maps.roll(). Bu sadece bir tercih.
rojo

bu yüzden kod () olmadan daha temiz görünmesini sağlamak için sadece sözdizimsel şeker. Neden seninle örnek olamadığını göremiyorummaps.roll()
Andreas

@Andreas Yapamayacağımı kim söyledi? Dediğim gibi, bu sadece düşüncelerimi organize etmeme yardımcı olmanın bir yolu. Kodlama bir sanattır. Bob Ross'a portakal kullanabildiği zaman neden yanmış hardal kullanmak zorunda olduğunu sormuyorsun. Şimdi bir ihtiyaç görmeyebilirsiniz, ancak bir gün resminizin biraz yanmış hardal ihtiyacı olduğuna karar verdiğinizde, paletinizde olacaktır.
rojo

:) i almak ve ayarlamak sözdizimi yapar görmek bir şey, bir özellik bir özelliği olarak kullanılırsa otomatik çalışma ediliyor.
Andreas

11

Harfler ve ayarlayıcılar gerçekten sadece sınıfların özel özelliklerine sahip olduğunuzda anlamlıdır. Javascript, normalde Nesne Tabanlı Diller'den düşündüğünüz gibi özel sınıf özelliklerine sahip olmadığından, anlaşılması zor olabilir. Özel sayaç nesnesine bir örnek. Bu nesne hakkında güzel olan şey, "count" iç değişkenine nesnenin dışından erişilememesidir.

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

Hala kafanız karıştıysa, Crockford'un Javascript'teki Özel Üyeler hakkındaki makalesine göz atın .


39
Katılmıyorum. Harfler ve ayarlayıcılar, tanımı basit bir değişken olmayan bilgileri kapsüllemek için de çok yararlıdır. Daha önce basit özellikler kullanan ve diğer şeylerin bağlı olabileceği bir sistemin davranışını değiştirmeniz gerekirse kullanışlı olabilir. Ayrıca, örneğin sadece işlev olan "sözde alıcıları" gösterir. Gerçek JavaScript alıcıları basit değerler olarak görünür (ve işlev gösterimi olmadan erişilir), dolayısıyla bunların gerçek gücü.
devios1

O kadar güçlü diyeceğime emin değilim. Bir şey görünen ancak X olarak Y'nin kesin olmamasına gerçekten. Kesinlikle var baz = foo.bararkasında gizli davranış tam bir şişmiş bir dizi olmasını beklemem . Ben ediyorum dan bekliyoruz foo.getBar()ancak.
AgmLauncher

8

Sanırım bağlantı verdiğiniz ilk makale bunu açıkça belirtiyor:

JavaScript'i bu şekilde yazmanın bariz avantajı, kullanıcının doğrudan erişmesini istemediğiniz belirsiz değerleri kullanabilmenizdir.

Buradaki amaç, alanları yalnızca a get()veya set()yöntemle erişime izin vererek özetlemek ve soyutlamaktır . Bu şekilde, alanı / verileri dahili olarak istediğiniz şekilde depolayabilirsiniz, ancak dış bileşenler yalnızca yayınlanan arayüzünüzden uzaktır. Bu, harici arayüzleri değiştirmeden dahili değişiklikler yapmanıza, set()yöntem içinde bazı doğrulama veya hata kontrolü yapmanıza vb.


6

Sıklıkla, herhangi bir erişim kontrolü olmadan genel mülklere sahip nesneleri görmeye alışkımız olmasına rağmen, JavaScript özellikleri doğru bir şekilde tanımlamamıza izin verir. Aslında, bir özelliğe nasıl erişilebileceğini ve ona hangi mantığa başvurabileceğimizi kontrol etmek için tanımlayıcıları kullanabiliriz. Aşağıdaki örneği düşünün:

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

Nihai sonuç:

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

Bu konuda kafa karıştırıcı olan şey ... getters, bir özelliği aldığınızda çağrılan işlevlerdir, seters, onu ayarladığınızda. örneğin, yaparsan

obj.prop = "abc";

Özellik pervanesini ayarlıyorsunuz, getters / setters kullanıyorsanız, argüman olarak "abc" ile setter işlevi çağrılır. Nesnenin içindeki ayarlayıcı işlev tanımı ideal olarak şöyle görünecektir:

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

Bunun tarayıcılarda ne kadar iyi uygulandığından emin değilim. Firefox'un çift altı çizili özel ("sihirli") yöntemlerle alternatif bir sözdizimi de var gibi görünüyor. Her zamanki gibi Internet Explorer bunların hiçbirini desteklemez.


2

Yapıcı prototipi ile js sınıfı için örnek yöntemi tanımlayabilirsiniz.

Örnek kod aşağıdadır:

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

Ve bu herhangi bir tarayıcı için çalışmalıdır, ayrıca bu kodu çalıştırmak için nodejs'i de kullanabilirsiniz.


1
Bu sadece yeni getName ve setName yöntemleri oluşturuyor. Bunlar mülk oluşturmakla ilgili değildir!
uzay95

2

Ayrıca okuduğum açıklama ile biraz kafam karıştı , çünkü yazmadığım mevcut bir prototife bir özellik eklemeye çalışıyordum, bu yüzden prototipi değiştirmek yanlış bir yaklaşım gibi görünüyordu. Yani, gelecek nesiller lastiçin Arrayşöyle bir özellik ekledim :

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

IMHO işlevi eklemekten çok daha güzel.


1

Erişimci kavramına atıfta bulunuyorsanız, basit amaç alttaki depolamayı keyfi manipülasyondan gizlemektir. Bunun için en uç mekanizma

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

Gerçek JS alıcı / ayarlayıcı özelliğine başvuruyorsanız, ör. defineGetter/ defineSetter, ya da { get Foo() { /* code */ } }, o zaman çoğu modern motorda, bu özelliklerin daha sonra kullanılmasının, aksi takdirde olduğundan çok daha yavaş olacağını belirtmek gerekir. Örneğin. performansını karşılaştır

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

Sizin için biraz çirkin olabilecek bir tane var, ama platformlar arasında iş bitiyor

function myFunc () {

var _myAttribute = "default";

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

bu şekilde, aradığınızda

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

Gerçekten bir şeyleri baharatlamak istiyorsanız .. bir tür kontrol ekleyebilirsiniz:

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

veya codingforums.com adresinde gelişmiş typeeof check: type.of () koduyla daha da çılgın olun


asıl nokta, ortak arayüzü değiştirmeye gerek kalmadan merak uyandıran bir şey için bir niteliği değiştirebilmektir. Bir çağrı () etiketi eklemek etiketi değiştirir.
Michael Scott Cuthbert
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.