Getters \ setters for dummies

133

Saya telah mencoba untuk mendapatkan kepalaku tentang getter dan setter dan itu tidak meresap. Saya telah membaca JavaScript Getters and Setters dan Mendefinisikan Getters and Setters dan tidak mendapatkannya.

Dapatkah seseorang dengan jelas menyatakan:

  1. Apa yang harus dilakukan oleh seorang pengambil dan penentu, dan
  2. Berikan beberapa contoh SANGAT sederhana?
Alexander Abakumov
sumber
12
Secara pribadi, saya tidak tahu bagaimana Anda bisa mendapatkan penjelasan yang lebih jelas daripada penjelasan John ...
Jason Bunting
Di dunia OO, pengambil dan penyetel membantu menulis kelas yang dienkapsulasi dengan benar
overexchange
Pada dasarnya saya melihatnya seperti ini: Anda mendefinisikan kelebihan untuk mendapatkan dan mengatur properti, dan kelebihan ini adalah fungsi; tetapi, Anda tidak perlu memanggil mereka. Dengan cara ini Anda dapat mengganti a = setValue(5);dengan a = 5;dan dengan setValue()demikian akan dipanggil di bawah tenda untuk melakukan apa pun yang Anda suka.
Andrew

Jawaban:

102

Selain jawaban @ millimoose , setter juga dapat digunakan untuk memperbarui nilai lainnya.

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

Sekarang, Anda dapat mengatur fullName, firstdan lastakan diperbarui dan sebaliknya.

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
sumber
2
@Akash: Tidak, meskipun, Internet Explorer 9 mendukung Object.definePropertyfungsi yang lebih baru yang dapat menentukan getter dan setter.
Matthew Crumley
9
Bukankah benar-benar menyakitkan bahwa MS tidak mendukung JS dengan benar dan mereka tidak membuat silverlight mereka berjalan di mana-mana, jadi saya harus memprogram semuanya dua kali, satu untuk SL dan satu untuk seluruh dunia :)
Akash Kava
2
@ Martin: Anda bisa menjadikannya pribadi dengan menggunakan teknik yang sama seperti pada jawaban John . Jika Anda ingin menggunakan getter / setter nyata, Anda harus menggunakan this.__defineGetter__atau Object.definePropertyfungsi yang lebih baru .
Matthew Crumley
1
Hanya satu masalah dengan pendekatan yang tercantum di atas, jika Anda ingin menambahkan getter dan setter untuk kelas yang sudah ada itu akan menimpa prototipe, dan metode asli tidak akan dapat diakses.
xchg.ca
1
Bukankah pendekatan ini menimpa Name.prototype.constructor? Sepertinya alternatif yang buruk untuk Jawaban millimoose .
r0estir0bbe
62

Getters dan Setter dalam JavaScript

Gambaran

Getters dan setter dalam JavaScript digunakan untuk mendefinisikan properti yang dihitung , atau accessor . Properti yang dihitung adalah properti yang menggunakan fungsi untuk mendapatkan atau menetapkan nilai objek. Teori dasarnya adalah melakukan sesuatu seperti ini:

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'

Ini berguna untuk secara otomatis melakukan hal-hal di belakang layar ketika properti diakses, seperti menjaga angka dalam jangkauan, memformat ulang string, memicu peristiwa nilai-telah-berubah, memperbarui data relasional, menyediakan akses ke properti pribadi, dan banyak lagi.

Contoh di bawah ini menunjukkan sintaks dasar, meskipun mereka hanya mendapatkan dan menetapkan nilai objek internal tanpa melakukan sesuatu yang istimewa. Dalam kasus dunia nyata Anda akan memodifikasi input dan / atau nilai output sesuai dengan kebutuhan Anda, seperti yang disebutkan di atas.

dapatkan / set Kata Kunci

Dukungan ECMAScript 5 getdan setkata kunci untuk mendefinisikan properti yang dihitung. Mereka bekerja dengan semua browser modern kecuali IE 8 dan di bawahnya.

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

Setter dan Setter Kustom

getdan setbukan kata-kata yang dilindungi undang-undang, sehingga kata-kata tersebut kelebihan beban untuk membuat fungsi properti terkomputer-sendiri milik browser Anda sendiri. Ini akan berfungsi di browser apa pun.

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

Atau untuk pendekatan yang lebih ringkas, satu fungsi dapat digunakan.

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

Hindari melakukan sesuatu seperti ini, yang dapat menyebabkan kode mengasapi.

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

Untuk contoh di atas, nama properti internal diabstraksi dengan garis bawah untuk mencegah pengguna dari hanya melakukan foo.barvs. foo.get( 'bar' )dan mendapatkan nilai "mentah". Anda dapat menggunakan kode kondisional untuk melakukan berbagai hal tergantung pada nama properti yang sedang diakses (melalui nameparameter).

Object.defineProperty ()

Menggunakan Object.defineProperty()adalah cara lain untuk menambahkan getter dan setter, dan dapat digunakan pada objek setelah ditetapkan. Ini juga dapat digunakan untuk mengatur perilaku yang dapat dikonfigurasi dan enumerable. Sintaks ini juga berfungsi dengan IE 8, tetapi sayangnya hanya pada objek 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 __ ()

Akhirnya, __defineGetter__()adalah pilihan lain. Ini sudah usang, tetapi masih banyak digunakan di web dan karenanya tidak akan hilang dalam waktu dekat. Ini bekerja di semua browser kecuali IE 10 dan di bawah. Meskipun opsi lain juga berfungsi dengan baik pada non-IE, jadi yang satu ini tidak begitu berguna.

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

Juga patut dicatat adalah bahwa dalam contoh-contoh terakhir, nama-nama internal harus berbeda dari nama pengakses untuk menghindari rekursi (yaitu, foo.barpanggilan foo.get(bar)memanggil foo.barpanggilan foo.get(bar)...).

Lihat juga

MDN dapatkan , atur , Object.defineProperty () , __defineGetter __ () , __defineSetter __ () Dukungan Getter
MSDN IE8

Beejor
sumber
1
Dalam pendekatan yang lebih kompak , this[ '_' + name ] = value;bisa this[ '_' + name ] = arguments[1];dan tidak perlu menentukan valuearg.
Redhart
1
Contoh: var foo = { bar : 123, get bar(){ return bar; }, set bar( value ){ this.bar = value; } }; foo.bar = 456; Meningkatkan pengecualian: Uncaught RangeError: Ukuran stack panggilan maksimum melebihi di Object.set bar [sebagai bar] (<anonymous>: 4: 32) di Object.set bar [as bar] (<anonymous>: 4: 32 ) di bar Object.set [sebagai bar] (<anonymous>: 4: 32) di bar Object.set [sebagai bar] (<anonymous>: 4: 32) di bar Object.set [[bar]] (<anonymous> : 4: 32) di bar Object.set [sebagai bar] (<anonymous>: 4: 32)
nevf
1
Nama yang ditetapkan / dapatkan harus berbeda dengan nama properti. Jadi alih-alih bar: 123dan this.bar = valuelain - lain ubahlah ini menjadi _bar misalnya. Lihat: hongkiat.com/blog/getters-setters-javascript
nevf
@nevf Terima kasih atas koreksinya! Ya, biasanya dengan properti yang dihitung yang internal "nyata" dinamai seperti _fooatau mFoo. Jika sama dengan pengambil / penyetel, itu akan menyebabkan loop tak terbatas karena rekursi dan kemudian Stack Overflow ™ ;-) karena ketika Anda mengatakan a = b, ia memanggil a.get (b) yang dengan sendirinya memanggil a = b , yang memanggil a.get (b), ...
Beejor
58

Anda akan menggunakannya misalnya untuk mengimplementasikan properti yang dihitung.

Sebagai contoh:

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
sumber
Ok, saya pikir saya mulai mengerti. Saya mencoba untuk menetapkan pengambil ke properti panjang objek array tetapi mendapatkan kesalahan: "Deklarasi ulang panjang var" Dan kode terlihat seperti ini: obj = []; obj .__ defineGetter __ ('length', function () {return this.length;});
1
Itu karena objek Array sudah memiliki properti panjang builtin. Jika deklarasi ulang diizinkan, menelepon panjang baru akan berulang tanpa batas. Coba panggil properti "my_length" atau semacamnya.
Millimoose
Untuk mendefinisikan kedua getter dalam satu pernyataan, gunakan Object.defineProperties.
r0estir0bbe
Tidak bisakah Anda membuat {"area": ​​function () {return ...}}? cukup tetapkan sebagai properti objek
RegarBoy
@developer Itu bukan pengambil Javascript seperti yang didefinisikan oleh bahasa, itu hanya sebuah fungsi. Anda harus memanggilnya untuk mendapatkan nilai, itu tidak membebani akses ke properti. Juga ada lingkaran neraka khusus yang diperuntukkan bagi orang yang menemukan sistem objek rusak mereka sendiri di JS alih-alih membangun yang sudah dimilikinya.
milimoose
16

Maaf membangkitkan pertanyaan lama, tapi saya pikir saya mungkin berkontribusi beberapa contoh yang sangat mendasar dan penjelasan for-dummies. Tidak ada jawaban lain yang diposting sejauh ini yang mengilustrasikan sintaksis seperti contoh pertama panduan MDN , yang kira-kira sama mendasarnya dengan yang didapat.

Getter:

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

console.log(settings.fullname);

... akan masuk John Smith, tentu saja. Seorang rajin berperilaku seperti properti objek variabel, tetapi menawarkan fleksibilitas fungsi untuk menghitung nilai yang dikembalikan dengan cepat. Ini pada dasarnya cara yang bagus untuk membuat fungsi yang tidak memerlukan () saat memanggil.

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

... akan masuk New Yorkke konsol. Seperti getter, setter dipanggil dengan sintaksis yang sama dengan menetapkan nilai properti objek, tetapi cara mewah lain untuk memanggil fungsi tanpa ().

Lihat jsfiddle ini untuk contoh yang lebih menyeluruh, mungkin lebih praktis. Melewati nilai ke setter objek memicu penciptaan atau populasi item objek lainnya. Secara khusus, dalam contoh jsfiddle, melewatkan array angka meminta setter untuk menghitung rata-rata, median, mode, dan rentang; lalu setel properti objek untuk setiap hasil.

rojo
sumber
Saya masih tidak mengerti manfaat menggunakan get and set vs membuat getMethod atau setMethod. Apakah satu-satunya manfaat yang dapat Anda panggil tanpa ()? Pasti ada alasan lain itu ditambahkan ke javascript.
Andreas
@Andreas Getters and setters berperilaku seperti properti ketika dipanggil, yang dapat membantu mengartikulasikan tujuan yang dimaksud. Mereka tidak membuka kunci jika tidak ada kemampuan yang hilang, tetapi penggunaannya dapat membantu Anda mengatur pikiran Anda. Itulah manfaat nyata. Sebagai contoh praktis, saya biasa menggunakan pengambil untuk memperluas objek Google Maps. Saya perlu menghitung sudut gulungan kamera sehingga saya bisa memutar ubin peta secara rata ke cakrawala. Google melakukan ini secara otomatis di bagian belakang sekarang; tetapi pada saat itu sangat membantu bagi saya untuk mengambil maps.rollsebagai properti daripada maps.roll()mengembalikan nilai. Itu hanya preferensi.
rojo
jadi itu hanya gula sintaksis untuk membuat kode terlihat lebih bersih tanpa (). Saya tidak mengerti mengapa Anda tidak bisa memberi contoh kepada Andamaps.roll()
Andreas
@ Andreas Siapa bilang saya tidak bisa? Seperti yang saya katakan, itu hanya cara untuk membantu saya menjaga pikiran saya teratur. Pengkodean adalah seni. Anda tidak bertanya kepada Bob Ross mengapa dia harus menggunakan oker bakar ketika dia bisa menggunakan jeruk. Anda mungkin tidak melihat kebutuhan sekarang, tetapi suatu hari ketika Anda memutuskan bahwa lukisan Anda membutuhkan sedikit oker terbakar, itu akan berada di palet Anda.
rojo
:) satu hal yang saya lihat bahwa dapatkan dan set tidak sintaks, sedang dijalankan secara otomatis jika digunakan sebagai properti properti.
Andreas
11

Getters dan setter benar-benar hanya masuk akal ketika Anda memiliki properti pribadi kelas. Karena Javascript tidak benar-benar memiliki properti kelas privat seperti yang biasa Anda pikirkan dari Bahasa Berorientasi Objek, mungkin sulit untuk dipahami. Ini adalah salah satu contoh objek penghitung pribadi. Yang menyenangkan tentang objek ini adalah bahwa variabel "jumlah" internal tidak dapat diakses dari luar objek.

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

Jika Anda masih bingung, lihat artikel Crockford tentang Anggota Pribadi dalam Javascript .

John
sumber
39
Saya tidak setuju. Getters dan setter juga sangat berguna untuk merangkum informasi yang definisinya mungkin bukan hanya variabel sederhana. Ini bisa berguna jika Anda perlu mengubah perilaku sistem yang sebelumnya menggunakan properti sederhana dan yang bergantung pada hal-hal lain. Selain itu, contoh Anda hanya menunjukkan "pseudo getter" yang hanya berfungsi. Pembuat JavaScript nyata muncul sebagai nilai sederhana (dan diakses tanpa notasi fungsi), karenanya kekuatan sebenarnya dari mereka.
devios1
Tidak yakin apakah saya akan menyebut itu kuat. Sesuatu muncul sebagai X tetapi benar-benar Y belum tentu jelas. Saya benar-benar TIDAK akan berharap var baz = foo.baruntuk memiliki set lengkap perilaku tersembunyi di baliknya. Saya akan berharap bahwa dari foo.getBar(), namun.
AgmLauncher
8

Saya pikir artikel pertama yang Anda tautkan menyatakannya dengan cukup jelas:

Keuntungan yang jelas untuk menulis JavaScript dengan cara ini adalah Anda dapat menggunakannya dengan nilai yang tidak Anda inginkan untuk diakses pengguna secara langsung.

Tujuannya di sini adalah untuk merangkum dan mengabstraksi bidang dengan hanya mengizinkan akses ke sana melalui suatu get()atau set()metode. Dengan cara ini, Anda dapat menyimpan bidang / data secara internal dengan cara apa pun yang Anda inginkan, tetapi komponen luar hanya jauh dari antarmuka yang Anda terbitkan. Ini memungkinkan Anda untuk membuat perubahan internal tanpa mengubah antarmuka eksternal, untuk melakukan validasi atau pengecekan kesalahan dalam set()metode, dll.

matt b
sumber
6

Meskipun sering kita terbiasa melihat objek dengan properti publik tanpa kontrol akses, JavaScript memungkinkan kita untuk menggambarkan properti dengan akurat. Bahkan, kita bisa menggunakan deskriptor untuk mengontrol bagaimana properti dapat diakses dan logika mana yang bisa kita terapkan padanya. Perhatikan contoh berikut:

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

Hasil akhir:

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
sumber
2

Yang sangat membingungkan tentang itu ... getter adalah fungsi yang dipanggil saat Anda mendapatkan properti, setter, saat Anda mengaturnya. Misalnya, jika Anda melakukannya

obj.prop = "abc";

Anda sedang mengatur properti prop, jika Anda menggunakan getter / setter, maka fungsi setter akan dipanggil, dengan "abc" sebagai argumen. Definisi fungsi setter di dalam objek idealnya akan terlihat seperti ini:

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

Saya tidak yakin seberapa baik yang diterapkan di seluruh browser. Tampaknya Firefox juga memiliki sintaks alternatif, dengan metode khusus ganda ("ajaib"). Seperti biasa Internet Explorer tidak mendukung semua ini.

Rolf
sumber
2

Anda dapat mendefinisikan metode instance untuk kelas js, melalui prototipe konstruktor.

Berikut ini adalah kode sampel:

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

Dan, ini harus bekerja untuk browser apa pun, Anda juga bisa menggunakan nodejs untuk menjalankan kode ini.

Eric Wang
sumber
1
Ini hanya membuat getName baru, dan metode setName. Ini tidak terkait dengan membuat properti!
uzay95
2

Saya juga agak bingung dengan penjelasan yang saya baca , karena saya mencoba menambahkan properti ke prototipe yang sudah ada yang tidak saya tulis, jadi mengganti prototipe sepertinya pendekatan yang salah. Jadi, untuk anak cucu, inilah cara saya menambahkan lastproperti ke Array:

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

Jauh lebih bagus daripada menambahkan fungsi IMHO.

shawkinaw
sumber
1

Jika Anda mengacu pada konsep pengakses, maka tujuan sederhananya adalah menyembunyikan penyimpanan yang mendasarinya dari manipulasi sewenang-wenang. Mekanisme paling ekstrem untuk ini adalah

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

Jika Anda merujuk ke fitur JS pengambil / penyetel sebenarnya, mis. defineGetter/ defineSetter, atau { get Foo() { /* code */ } }, maka perlu dicatat bahwa dalam kebanyakan mesin modern penggunaan selanjutnya dari sifat-sifat itu akan jauh lebih lambat daripada yang seharusnya. misalnya. membandingkan kinerja

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
sumber
-1

Saya punya satu untuk kalian yang mungkin sedikit jelek, tapi itu bisa dilakukan di semua platform

function myFunc () {

var _myAttribute = "default";

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

dengan cara ini, saat Anda menelepon

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

Jika Anda benar-benar ingin memperbaikinya .. Anda dapat memasukkan jenis cek:

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

atau lebih gila lagi dengan kode typeof check: type.of () yang canggih di codingforums.com

artsy.ca
sumber
intinya adalah untuk dapat mengubah atribut ke sesuatu yang lebih menarik tanpa perlu mengubah antarmuka publik. Menambahkan tag panggilan () mengubahnya.
Michael Scott Cuthbert