Objelerin özellikleri saklayabildiğini biliyorsunuz.
Şimdiye kadar özellik basit “anahtar-değer” ikilisiydi. Fakat objenin özelliği aslında bundan daha karmaşık ve daha farklılaştırılabilir özellikler taşımaktadır.
Özellik Bayrakları
Obje özellikleri değer dışında, 3 özelliğe sahiptir ( bunlara “bayraklar” denir. )
yazılabilir– eğertrueise değiştirilebilir aksi halde sadece okunabilir.sayılabilir– eğertrueise döngü içinde listelenmiştir, aksi halde listelenmemiştir.ayarlanabilir– eğertrueise özellik silinebilir ve nitelikler ( attributes ) değiştirilebilir, diğer türlü değiştirilemez.
Bunları henüz görmediniz, genel olarak da zaten pek gösterilmezler. Bir özellik yarattığınızda “normal yolla” bu değerlerin tümü true olarak ayarlanır. Fakat biz bunları istediğimiz zaman değiştirebiliriz.
İlk önce bu bayraklar nasıl alınır buna bakalım:
Object.getOwnPropertyDescriptor metodu bir özellik hakkındaki tüm bilgilerin sorgulanabilmesini sağlar.
Yazımı:
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
obj- Bilgi alınacak obje
propertyName- Özelliğin ismi
Buradan dönen bir değer döner buna “özellik tanımlayıcısı” denir. Bu obje tüm bayrak bilgilerini içerir.
Örneğin:
let user = {
name: "John"
};
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
/* property descriptor:
{
"value": "John",
"writable": true,
"enumerable": true,
"configurable": true
}
*/
Bayrakları değiştirmek için Object.defineProperty kullanılabilir.
Yazımı:
Object.defineProperty(obj, propertyName, descriptor)
obj,propertyName- Üzerinde çalışılacak obje ve özellik.
descriptor- Uygulanacak özellik tanımlayıcı
Eğer özellik var ise defineProperty bu özelliğin bayraklarını günceller. Diğer türlü, bu özelliği yaratır ve verilen bayrakları ayarlar. Bu durumda eğer bayrak verilmemiş ise false kabul edilir.
Örneğin, aşağıda tüm bayrakları false olarak tanımlanmış bir name özelliğini görmektesiniz.
let user = {};
Object.defineProperty(user, "name", {
value: "John"
});
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
"value": "John",
"writable": false,
"enumerable": false,
"configurable": false
}
*/
Bunu “normal yoll” yaratılmış user.name ile karşılaştırdığınızda tüm bayrakların false olduğunu görebilirsiniz. Eğer bizim istediğimiz bu değilse bunları true yapmakta fayda var.
Şimdi bu bayrakların etkilerini inceleyebiliriz.
Salt Oku
user.name’i sadece okunabilir yapmak için writable bayrağının değiştirilmesi gerekir.
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
writable: false
});
user.name = "Pete"; // Error: Salt okunur özelliğe değer atanamaz.
Artık kimse kendi defineProperty metodunu yazmadıkça kullanıcının ismini değiştiremez.
Aynı işlem bir özellik olmadığı durumda:
let user = { };
Object.defineProperty(user, "name", {
value: "Pete",
// yeni özellikler için neyin doğru olduğu özellikle belirtilmelidir.
enumerable: true,
configurable: true
});
alert(user.name); // Pete
user.name = "Alice"; // Error
Non-enumerable
Şimdi user’a toString metodu ekleyelim.
Normalde toString objeler için non-enumerable’dır yani for ile objenin özelliklerini dönerken görünmez. Fakat bu özellikği kendiniz eklerseniz for..in içeriisnde görünür. Şu şekilde:
let user = {
name: "John",
toString() {
return this.name;
}
};
// Varsayılan olarak, var olan özelliklerimiz görünecektir.
for(let key in user) alert(key); // name, toString
Eğer beğenmiyorsanız, enumerable:false’u ayarlayabilirsiniz. Böylece for..in döngüsünün içerisinde normalde olduğu gibi görünmez olur:
let user = {
name: "John",
toString() {
return this.name;
}
};
Object.defineProperty(user, "toString", {
enumerable: false
});
// Artık toString görünmeyecektir:
for(let key in user) alert(key); // name
Non-enumerable özellikler de Object.keys’den çıkarılacaktır:
alert(Object.keys(user)); // name
Non-configurable ( Ayarlanamaz )
configurable:false bayrağı bazen varsayılan objeler ve özellikler için standart olarak gelir.
Bir ayarlanamayan özellik silinemez veya defineProperty ile değiştirilemez.
Örneğin, MATH.PI hem sadece okunabilir, hem döngü içinde görünmez ( non-enumerable) hem de değiştirilemez:
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');
alert( JSON.stringify(descriptor, null, 2 ) );
/*
{
"value": 3.141592653589793,
"writable": false,
"enumerable": false,
"configurable": false
}
*/
Öyleyse, Math.PI hem değiştirilemez hem de üzerine yazılamaz.
Math.PI = 3; // Hatta
// delete Math.PI 'de çalışmayacaktır.
Bir özelliği değiştirilemez yapmak tek yönlü bir yoldu. Bunu geri çeviremeyiz çünkü defineProperty ayarlanamaz özellikler üzerinde çalışmaz.
Burada user.name tamamen mühürlü bir sabit yapılmaktadır:
let user = { };
Object.defineProperty(user, "name", {
value: "John",
writable: false,
configurable: false
});
// user.name veya bayrağı değiştirilemez.
// hiçbiri çalışmayacaktır:
// user.name = "Pete"
// delete user.name
// defineProperty(user, "name", ...)
Object.defineProperty(user, "name", {writable: true}); // Error
Sıkı olmayan modda, sadece okunabilir özelliklerin üzerine yazarsanız bir hata görmezsiniz. Fakat yine de işleminiz başarılı olmaz. Yapmamanız gereken bir aksiyonda sadece görmezden gelinir.
Object.defineProperties
Object.defineProperties(obj, descriptors) metodu birçok metodun tek bir seferde tanımlanmasını sağlar.
Yazımı:
Object.defineProperties(obj, {
prop1: descriptor1,
prop2: descriptor2
// ...
});
Örneğin:
Object.defineProperties(user, {
name: { value: "John", writable: false },
surname: { value: "Smith", writable: false },
// ...
});
Öyleyse, birçok özelliği tek bir seferde tanımlayabiliriz.
Object.getOwnPropertyDescriptors
Tüm özelliklerin tanımlarını bir defada almak için Object.getOwnPropertyDescriptors(obj) metodunu kullanabilirsiniz.
Object.defineProperties ile birlikte “bayrak-farkında” olacak şekilde objenin klonlanması için kullanılabilir:
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
Normalde obje klonlandığında, atama ile özellikler aşağıdaki gibi kopyalanmalıdır:
for(let key in user) {
clone[key] = user[key]
}
…Fakat bu bayrakları kopyalamaz. Eğer “daha iyi” bir klon istenirse Object.defineProperties tercih edilmelidir.
Diğer bir fark ise for..in sembolik özellikleri görmezden gelir. Fakat Object.getOwnPropertyDescriptors tüm özellikleri, sembolik olanlar dahil, dönderir.
Objeleri globalde kilitlemek
Özellik tanımları tekil özellikler seviyesinde çalışır.
Bunun ile birlikte tüm objeyi limitleyen metodlar bulunmaktadır:
- Object.preventExtensions(obj)
- Objeye özelliklerin eklenmesini engeller.
- Object.seal(obj)
- Özellikleri ekleme ve silmeyi engeller. Var olan tüm özellikler için
configurable: falseolarak ayarlar. - Object.freeze(obj)
- Özellikerin eklenmesini, silinmesini ve değiştirilmesini engeller, var olan tüm özellikler için
configurable:false, writable:falseayarlanır.
Bunlar için testsler vardır:
- Object.isExtensible(obj)
- Eğer özellik engellenmiş ise
falseaksi haldetruedönderilir. - Object.isSealed(obj)
- Eğer özellik ekleme/silme engellenmiş ise
true, tüm var olan özelliklerconfigurable: false’e sahipse. - Object.isFrozen(obj)
- Eüer özellik ekleme/silme/değiştirme engellenmiş ve tüm özellikler
configurable:false, writable:falseisetruedöndür.
Bu metodlar pratikte çok az kullanılır.
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)