Bu bölümde prototipler ile çalıÅmak için ek metodlardan bahsedeceÄiz
Bizim bildiÄimizin haricinde prototipi ayarlamak ve almak için baÅka yöntemler de bulunmaktadır:
- Object.create(proto[, descriptors]) â verilen
protoâyu[[Prototype]]Åeklinde alarak ve opsiyonel tanımlayıcı özelliÄi kullanarak boÅ bir obje oluÅturur. - Object.getPrototypeOf(obj) â
objânin[[Prototype]]'ını döndürür. - Object.setPrototypeOf(obj, proto) â
objânin[[Prototype]]'ınıprotoâya ayarlar.
ÃrneÄin:
let animal = {
eats: true
};
// animal prototipi ile yeni bir rabbit objesi yaratma.
let rabbit = Object.create(animal);
alert(rabbit.eats); // true
alert(Object.getPrototypeOf(rabbit) === animal); // rabbit'in prototipini alma
Object.setPrototypeOf(rabbit, {}); // rabbit'in prototipini {}'e çevirme.
Object.create opsiyonel olarak ikinci bir argümana sahiptir: özellik tanımlayıcı. AÅaÄıdaki gibi yeni objeye özellikler ekleyebiliriz:
let animal = {
eats: true
};
let rabbit = Object.create(animal, {
jumps: {
value: true
}
});
alert(rabbit.jumps); // true
Tanımlayıcıların Ãzellik bayrakları ve tanımlayıcılar bölümünde üstünden geçilmiÅtir. Formatları aynıdır.
Object.create kullanarak obje klonlama iÅlemini yapabiliriz. Bu objenin özelliklerini for..in ile dolanmaktan daha etkin bir yöntemdir.
// Objenin yüzeysel klonu
let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
Bu tam olarak objânin aynısını verir. Tüm özellikler: dönülebilir veya dönülemez, veri özellikleri, alıcı ve ayarlayıcılar â her Åey, ayrıca doÄru [[Prototype]] ile
Tarihçe
[[Prototype]]'ı ayarlayabileceÄimiz yöntemleri saymaya kalsak baya zorluk yaÅarız. Ãok fazla yöntem bulunmaktadır.
Neden?
Birçok nedeni bulunmaktadır.
- Yapıcının
"prototype"özelliÄi ilk javascript ortaya çıktıÄından beri bulunmaktadır. - 2012âde:
Object.createbir standart olarak oturdu. Bu verilen prototip ile objeleri yaratmaya izin verdi. Fakat bunları almaya veya ayarlamaya deÄil. Bundan dolayı tarayıcılar bir standart olmayan__proto__eriÅimcilerini uygulayarak alıcı ve ayarlayıcılara ( get/set)'e izin verdi. - 2015âte:
Object.setPrototypeOfveObject.getPrototypeOfbir standart olarak eklendi.__proto__defakto Åeklinde aslında her yerde kullanılmıÅtı, Bundan dolayı çokta kulllanılan özellikler olmadı, sadece tarayıcı harici çevrelerde kullanılır oldu.
Artık bunların hepsi bizim kullanımımızdadır.
Teknik olarak [[Prototype]]'ı istediÄimiz an alma/ayarlama iÅi yapabiliriz. Fakat genelde bunu sadece obje yaratırken kullanır ve daha sonra düzenleme yapmayız: rabbit, animaldan kalıtım alır fakat onu deÄiÅtirmez. JavaScript motorları da bunu yüksek derecede optimize edebilir. Prototipi Object.setPrototypeOf veya obj.__proto__ ile sonradan deÄiÅtirmek oldukça yavaÅ bir operasyondur. Ama mümkündür.
âEn basitâ Objeler
BildiÄiniz gibi objeler anahtar/deÄer ikilisini tutan iliÅkisel dizi Åeklinde kullanılabilir.
â¦EÄer içerisinde kullanıcı-kaynaklı anahtarlar ( örneÄin kullanıcının girdiÄi sözlük tipi veriler) var ise, ilginç bir aksaklık meydana gelir: "__proto_" haricinde tüm anahtarlar doÄru çalıÅır.
ÃrneÄin:
let obj = {};
let key = prompt("What's the key?", "__proto__");
obj[key] = "some value";
alert(obj[key]); // [object Object] olarak döner , "some value" deÄil!
EÄer kullanıcı tipleri __proto__ içerisinde ise atama görmezden gelinir!
Bu aslında çok da sürpriz olmasa gerek. __proto__ farklı bir özelliktir: Ya obje olur veya null, mesela bir karakter dizisi prototip olamaz.
Fakat buradaki amacımız böyle bir davranıÅı uygulamak deÄildi, deÄil mi? Biz key/value ikililerini kaydetmekti. "__proto__" anahtarı düzgün bir Åekilde kaydedilmedi. Bundan dolayı bu bir bugdır. Burada etkisi berbat deÄildir fakat diÄer durumlarda prototip gerçekten deÄiÅebilir ve çalıÅma tamamen istenmeyen bir sonuca varabilir.
Daha kötüsü â genelde geliÅtiriciler böyle bir ihtimali düÅünmezler bile. Bundan dolayı böyle bir bugâlar fark edilebilir ve saldırıya açık hale gelirler, özellikle JavaScript server tarafında kullanıldıysa.
Böyle bir olay sadece __proto__'da meydana gelir diÄer tüm özellikler normalde âatanabilirâ'dir.
Bu problemden nasıl kaçınılabilir?
Ãncelikle Map kullanılabilir, her Åey doÄru çalıÅır.
Fakat burada bize Obje yardımcı olabilir, çünkü dili yaratıcılar bu konuları uzun zaman önce düÅünmüÅler.
__proto__ objenin bir özelliÄi deÄildir. Fakat Object.prototypeâa eriÅim saÄlar (accessor):
Bundan dolayı, EÄer obj.__proto__ okunur veya atanırsa, ilgili alıcı/ayarlayıcı prototipten çaÄırılır, böylece [[Prototoy]] alınır/ayarlanır.
BaÅlangıçta __proto__ , [[Prototype]]a eriÅmek için bir yol olarak tanımlanmıÅtır, [[Prototype]] deÄil.
EÄer, eÄer objeyi iliÅkisel dizi olarak kullanmak istiyorsanız Åu Åekilde yapabilirsiniz:
let obj = Object.create(null);
let key = prompt("What's the key?", "__proto__");
obj[key] = "some value";
alert(obj[key]); // "some value"
Object.create(null) prototipâi olmayan boÅ bir obje yaratır.([[Prototype]] nullâdur):
Bundan dolayı __proto__ için atadan kalan alıcı/ayarlayıcı bulunmamaktadır. Artık sıradan bir veri özelliÄi olarak iÅlenir, bundan dolayı yukarıdaki örnek doÄru bir Åekilde çalıÅır.
Böyle objelere âen basitâ veya âsaf sözlük objeleriâ denir, Ãünkü bunlar sıradan objelerden {...} bile daha basittirler.
Bu objelerin kötü tarafı ise, içinde hiçbir varsayılan metod bulunmaz, ÃrneÄin: toString:
let obj = Object.create(null);
alert(obj); // Error (no toString)
â¦Fakat bu genelce iliÅkili diziler için problem oluÅturmaz.
ÃoÄu obje-baÄlantılı metodlar Object.something(...) Åeklindedir ve Object.keys(obj) gibi olduÄundan prototipte yer almazlar, bundan dolayı Åu Åekilde çalıÅmaya devam ederler:
let chineseDictionary = Object.create(null);
chineseDictionary.hello = "ni hao";
chineseDictionary.bye = "zai jian";
alert(Object.keys(chineseDictionary)); // hello,bye
Tüm özellikleri alma
Bir objeden Anahtar/deÄer ikilisini almak için birçok yol bulunmaktadır.
AÅaÄıdaki kullanımını zaten biliyorsunuz:
- Object.keys(obj) / Object.values(obj) / Object.entries(obj) â kendi adı/deÄerleri/anahtar-deÄer ikilileri Åeklinde döngü yapılabilir. Metodları sadece enumerable ( döngülenebilir ) ve anahtarları karakter dizisi olanlar .
EÄer sembolik özellikler istenirse:
- Object.getOwnPropertySymbols(obj) â kendi sembolik özellik isimlerini döndürür.
EÄer döngülenemez özellikleri istenirse:
- Object.getOwnPropertyNames(obj) â kendine ait tüm karakter dizisi özellikleri dizi olarak döner.
EÄer tüm özellikler istenir ise:
- Reflect.ownKeys(obj) â kendine ait tüm karakter dizisi özellikleri dizi olarak dönerâ¦
Bu metodlar hangi özellikleri döndürecekleri hususunda farklılıkları olsa da hepsi objenin kendi üzerinde çalıÅır. Prototipte bulunan özellikler listelenmez.
for..in döngüsü ise biraz farklıdır: KalıtılmıŠözelliklerâi de döngüye alır.
ÃrneÄin:
let animal = {
eats: true
};
let rabbit = {
jumps: true,
__proto__: animal
};
// Sadece kendi anahtarları
alert(Object.keys(rabbit)); // jumps
// kalıtılmıŠanahtarları da içerir.
for(let prop in rabbit) alert(prop); // jumps, sonra eats
EÄer kalıtılmıŠözellikler ayrıÅtırılmak istenirse bunun için varolan obj.hasOwnProperty(key) kullanılabilir: EÄer obj key adında kalıtımsal olmayan bir özelliÄe sahipse true dönderir.
Kalıtımsal özellikleri bu Åekilde filtreleyebilir veya baÅka bir Åey yapabiliriz:
let animal = {
eats: true
};
let rabbit = {
jumps: true,
__proto__: animal
};
for(let prop in rabbit) {
let isOwn = rabbit.hasOwnProperty(prop);
alert(`${prop}: ${isOwn}`); // jumps:true, then eats:false
}
Åu Åekilde kalıtım zinciri oluÅmuÅtur: rabbit, sonra animal ve en son Object.prototype ( çünkü animal tam objedir {â¦} ), sonra bunun üzerine null:
Zincire bakarsanız rabbit.hasOwnProperty nereden geliyor görebilirsiniz. Object.prototype.hasOwnPropery, diÄer bir deyiÅle kalıtılmıÅ.
⦠Fakat öyleyse neden for..in içerisinde görünmüyor. Halbuki for..in ile kalıtılmıŠtüm özelliklerin listeleneceÄini söylemiÅtik. Aslında cevap basit, bu döngüye alınamaz. Tıpkı diÄer Object.prototypeâlarda olduÄu gibi. Bundan dolayı listelenmemiÅtir.
Ãzet
Bu bölümde anlatılanların üzerinden kısaca geçecek olursak:
- Object.create(proto[, descriptors]) â verilen
protoile yeni bir obje yaratır, ayrıca opsiyonel olarak özellik tanımlıyıcılar verilebilir. - Object.getPrototypeOf(obj) â
objânin[[Prototype]]ını döner (__proto__alıcısı (getter) ile aynı iÅi yapar)). - Object.setPrototypeOf(obj, proto) â
objânin[[Prototype]]'ını verilenprotoâya ayarlar. (__proto__ayarlayıcısı (setter) ile aynı iÅi yapar) - Object.keys(obj) / Object.values(obj) / Object.entries(obj) â döngülenebilir karakter dizisi/ deÄerler/ anahtar-deÄer ikilisi dizisi döner.
- Object.getOwnPropertySymbols(obj) â tüm sembolik özelliklerin dizisini döner.
- Object.getOwnPropertyNames(obj) â özelliklerin tüm karakter dizisi isimlerini dizi olarak döner.
- Reflect.ownKeys(obj) â tüm özelliklerin isimlerini dizi olarak döner.
- obj.hasOwnProperty(key): EÄer
objkalıtılmıŠdeÄilse vekeyadında bir özelliÄi varsatruedöner.
Åunu da belirtmiÅ olalım __proto__ [[Prototype]] için alıcı/ayarlayıcıdır. Bu Object.prototype içerisinde yer alır, tıpkı diÄer metodlar gibi.
Prototip olmadan bir objeyi Object.create(null) Åeklinde yaratmak mümkündür. Bu tür objeler âsaf dictionary yapısıâ olarak kullanılır. "__proto__" anahtarı ile bir problemi bulunmamaktadır.
Obje özelliklerini döndüren ( Object.keys ve diÄerleri ) â âkendiâ özelliklerini döndürür. EÄer kalıtılmıŠolanlarını da istersek for..in kullanabiliriz.
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)