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