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