İki türlü özellik mevcuttur.
Bunlardan ilki veri özellikleri. Bunu zaten biliyorsunuz. Aslında Åimdiye kadar kullandıÄınız tüm özellikler veri özellikleriâdir.
İkinci tip özellikler ise eriÅim özellikleriâdir. Bunlar deÄerleri almak(get) ve ayarlamak(set) için kullanılan fonksiyonlardır, dıÅarıdaki koddan sanki bir özellikmiÅ gibi görünürler.
Alıcılar ve Ayarlayıcılar
EriÅim özellikleri âalıcıâ ve âayarlayıcıâ metodlar ile tanımlanır. Obje içerisinde tam olarak get ve set Åeklinde belirtilir:
let obj = {
get propName() {
// obj.propName yazıldıÄında burası çalıÅır.
},
set propName(value) {
// ayarlayıcı obj.propName = value ayarlandıÄında burası çalıÅır.
}
};
Alıcı metodlar obj.propName okunduÄunda, ayarlayıcı metodlar ise atama yapıldıÄında çalıÅır.
ÃrneÄin user objemiz olsun ve bunun name ve surname özellikleri olsun:
let user = {
name: "John",
surname: "Smith"
};
âfullNameâ adında bir özellik eklemek istenirser, elbette var olan kodu kopyala yapıÅtır yapmayacaÄız bunun yerine eriÅim özelliÄi kullanabiliriz:
let user = {
name: "John",
surname: "Smith",
get fullName() {
return `${this.name} ${this.surname}`;
}
};
alert(user.fullName); // John Smith
DıÅardan, bu özellik normal görünür. Aslında fikir de tam olarak budur. Biz user.fullName 'i fonksiyon olarak çaÄırmıyoruz. Onu normal bir Åekilde özellikmiÅ gibi okuyoruz. Alıcı perdenin arkasında çalıÅıyor.
Åu anda fullNameâin sadece alıcısı var. EÄer user.fullName= Åeklinde atamaya çalıÅırsanız hata alırsınız.
Bunu düzeltmek için ayarlayıcı metodu eklemek gerekmektedir:
let user = {
name: "John",
surname: "Smith",
get fullName() {
return `${this.name} ${this.surname}`;
},
set fullName(value) {
[this.name, this.surname] = value.split(" ");
}
};
// FullName ayarlandı.
user.fullName = "Alice Cooper";
alert(user.name); // Alice
alert(user.surname); // Cooper
Åimdi yeni bir âsanalâ özelliÄimiz oldu. Okunabiliyor, yazılabiliyor fakat aslında yok.
Bir özellik ya âveri özelliÄiâ ya da âeriÅim özelliÄiâ olabilir, aynı anda ikisi olamaz.
Bir özellik get prop() ile veya set prop() ile tanımlanmıÅsa, artık eriÅim özelliÄidir. Bundan dolayı okuyabilmek için alıcı ve atama yapabilmek için ayarlayıcı olması gerekir.
Bazen sadece ayarlayıcı veya alıcı olabilir. Fakat böyle bir durumda özellik okunabilir veya yazılabilir olmaz. Her ikisinin de yazılmıŠolması gerekir.
EriÅim Tanımlayıcıları
EriÅim tanımlayıcıları normal veri özelliklerine göre daha farklıdır.
EriÅim özellikleri için deger ve yazılabilir yoktur, bunun yerine get ve set fonksiyonları vardır.
Ãyleyse eriÅim tanımlayıcıları Åunlara sahiptir:
getâ parametresi olmayan fonksiyon, sadece özellik okunduÄunda çalıÅır.setâ bir parametreli fonksiyon, özellik ayarlanmak istendiÄinde çalıÅır.enumerableâ bu veri özellikleri ile aynıdır.configurableâ bu veri özellikleri ile aynıdır.
ÃrneÄin fullName ve definePropery eriÅim tanımlayıcıları için get ve setâi iletebiliriz.
let user = {
name: "John",
surname: "Smith"
};
Object.defineProperty(user, 'fullName', {
get() {
return `${this.name} ${this.surname}`;
},
set(value) {
[this.name, this.surname] = value.split(" ");
}
});
alert(user.fullName); // John Smith
for(let key in user) alert(key);
Tekrar hatırlatmakta fayda var, bir özelliklik ya eriÅim özelliÄi veya veri özelliÄi olabilir, ikisi aynı anda olamaz.
Aynı tanımlayıcıda eÄer hem get hem de value deÄerini kullanırsak aÅaÄıdaki hata meydana gelir:
// Error: Invalid property descriptor.
Object.defineProperty({}, 'prop', {
get() {
return 1
},
value: 2
});
Akıllıca getters/setters kullanmak
Getter/Setter âgerçekâ özelliklerin üzerinde daha iyi kontrol amacıyla kurulabilir.
ÃrneÄin, user gibi çok kısa isimler için name özelliÄini _name içerisinde tutabilirsiniz. Sonrasında atamaları setterâda filteleyebilirsiniz:
let user = {
get name() {
return this._name;
},
set name(value) {
if (value.length < 4) {
alert("İsim çok kısa, en az 4 karakter olmalıdır.");
return;
}
this._name = value;
}
};
user.name = "Pete";
alert(user.name); // Pete
user.name = ""; // İsim çok kısa...
Teknik olarak, dıÅarıdan hala user._name ile eriÅilebilir. Fakat genel bir kural olarak "_" ile baÅlayan özellikler içte kullanılan deÄiÅkenlerdir ve dıÅarıdan hiçbir zaman eriÅilmemelidir.
Uyumluluk için kullanma
Getter/setter fikrinin amacı aslında ânormalâ veri özelliklerinin kontrolünü her an elde tutabilmektir.
ÃrneÄin, kullanıcı objesini name ve age özellikleri ekleyelim:
function User(name, age) {
this.name = name;
this.age = age;
}
let john = new User("John", 25);
alert( john.age ); // 25
⦠Bu ilerde muhtemeldir deÄiÅebilir. ÃrneÄin age yerine ileride birthday verisi tutmak istebiliriz, böylece daha kesin yaÅ bilgisi tutulabilir:
function User(name, birthday) {
this.name = name;
this.birthday = birthday;
}
let john = new User("John", new Date(1992, 6, 1));
Peki eski age özelliÄi ne olacak ?
Her yerde bunu arayıp düzeltebiliriz, fakat bu zaman alır ve kod baÅkaları tarafından yazıldıysa zor olur. Ayrıca user objesinin içinde age özelliÄi pek de fena bir fikir sayılmaz, deÄil mi? Aslında bazı yerlerde tam da istediÄimiz ageâdir.
age için bir getter yazmak aslında bu problemi ortadan kaldırır.
function User(name, birthday) {
this.name = name;
this.birthday = birthday;
// yaÅ, geçerli tarih ve doÄum gününden hesaplanır
Object.defineProperty(this, "age", {
get() {
let todayYear = new Date().getFullYear();
return todayYear - this.birthday.getFullYear();
}
});
}
let john = new User("John", new Date(1992, 6, 1));
alert( john.birthday ); // birthday'e
alert( john.age ); // ... ve yaÅa aynı anda eriÅilebilir.
Åimdi eski kod da çalıÅır, ayrıca yeni bir özelliÄe de sahip olmuÅ oluruz.
Yorumlar
<code>kullanınız, birkaç satır eklemek için ise<pre>kullanın. EÄer 10 satırdan fazla kod ekleyecekseniz plnkr kullanabilirsiniz)