Ders 04 / 17
Özellik Tanımlayıcıları
Bir özelliğin değerinin yanında taşıdığı yazılabilirlik, sayılabilirlik ve yapılandırılabilirlik bayrakları; erişimci özellikler ve tanımlayıcıları koruyan kopyalama.
İçindekiler
Önceki ders iki kez aynı yere geldi: sınıf yöntemleri for...in çıktısında görünmüyor,
elle yazılan atamalar görünüyordu; ve okunanDeger bir okuma gibi yazıldığı hâlde
fonksiyon çalıştırıyordu. İkisi de tek bir gerçeğin sonucudur.
Bir özellik yalnızca “ad → değer” çiftinden ibaret değildir. Her özelliğin yanında, davranışını belirleyen bir özellik tanımlayıcısı (property descriptor) durur. Bu ders tanımlayıcının alanlarını tek tek açar; ardından değer yerine fonksiyon taşıyan erişimci özellikleri ele alır.
Tanımlayıcıyı Okumak
Object.getOwnPropertyDescriptor, bir nesnenin kendi özelliğinin tanımlayıcısını
döndürür. Veri özelliklerinde dört alan bulunur: value, writable, enumerable,
configurable.
function bayraklar(nesne, ad) { const tanim = Object.getOwnPropertyDescriptor(nesne, ad); if (tanim === undefined) return `${ad}: tanim yok`; if ("value" in tanim) { return `${ad}: veri, yazilabilir=${tanim.writable}, sayilabilir=${tanim.enumerable}, yapilandirilabilir=${tanim.configurable}`; } return `${ad}: erisimci, okuyucu=${typeof tanim.get}, yazici=${typeof tanim.set}, sayilabilir=${tanim.enumerable}`; } class OlcumKaydi { constructor(sensor, deger) { this.sensor = sensor; this.deger = deger; } bicimle() { return `${this.sensor}: ${this.deger}`; } } const kayit = new OlcumKaydi("S-01", 21.4); const duzKayit = { sensor: "S-02", deger: 19.8 }; Object.defineProperty(duzKayit, "birim", { value: "santigrat" }); console.log(bayraklar(kayit, "sensor")); console.log(bayraklar(OlcumKaydi.prototype, "bicimle")); console.log(bayraklar(duzKayit, "sensor")); console.log(bayraklar(duzKayit, "birim")); console.log(bayraklar(kayit, "bicimle"));
sensor: veri, yazilabilir=true, sayilabilir=true, yapilandirilabilir=true bicimle: veri, yazilabilir=true, sayilabilir=false, yapilandirilabilir=true sensor: veri, yazilabilir=true, sayilabilir=true, yapilandirilabilir=true birim: veri, yazilabilir=false, sayilabilir=false, yapilandirilabilir=false bicimle: tanim yok
Üç kural çıkar. Sıradan atamayla ya da nesne değişmeziyle üretilen özelliklerin üç bayrağı
da açıktır. Sınıf yöntemleri sayılamaz olarak tanımlanır — önceki dersteki for...in
farkının kaynağı budur. Object.defineProperty çağrısında belirtilmeyen her bayrak
kapalı sayılır; bu yüzden birim özelliği yazılamaz, sayılamaz ve yapılandırılamaz
çıkmıştır. Son satır ise işlevin adındaki “kendi” sözcüğünü hatırlatır: bicimle
prototipte durduğu için kayıt üzerinde tanımı yoktur.
Yazılabilirlik
writable bayrağı kapalıysa atama değeri değiştirmez. Değiştirmemenin iki farklı biçimi
vardır ve aradaki fark, kodun katı kipte çalışıp çalışmadığına bağlıdır.
const kayit = { sensor: "S-01", deger: 21.4 }; Object.defineProperty(kayit, "birim", { value: "santigrat", writable: false, enumerable: true, configurable: false, }); console.log(kayit.birim); try { kayit.birim = "fahrenhayt"; } catch (hata) { console.log(`${hata.constructor.name} yakalandi`); } console.log(kayit.birim); function katiOlmayanYazma(nesne) { return new Function("n", "n.birim = 'fahrenhayt'; return n.birim;")(nesne); } console.log(katiOlmayanYazma(kayit)); try { Object.defineProperty(kayit, "birim", { value: "kelvin" }); } catch (hata) { console.log(`${hata.constructor.name} yakalandi`); } console.log(kayit.birim);
santigrat TypeError yakalandi santigrat santigrat TypeError yakalandi santigrat
Modül dosyaları ve sınıf gövdeleri katı kipte çalışır; orada yazılamayan özelliğe atama
hata yükseltir. new Function ile üretilen gövde katı kipte değildir, bu yüzden aynı
atama sessizce başarısız olur — dördüncü satırdaki değişmemiş değer bunu gösterir. Sessiz
başarısızlık, hatanın atamadan çok sonra fark edilmesine yol açtığı için katı kip
tercih edilir.
Son iki satır configurable bayrağının etkisidir. Yapılandırılabilirlik kapatıldığında
tanımlayıcı bir daha değiştirilemez ve özellik silinemez; bu, geri alınamayan tek
bayraktır. Bu nedenle configurable: false bilinçli bir karar olmalıdır.
Sayılabilirlik
enumerable bayrağı, özelliğin listelemelere girip girmeyeceğini belirler. Etkisi geniş
bir işlem kümesine yayılır.
const kayit = { sensor: "S-01", deger: 21.4 }; Object.defineProperty(kayit, "kaynakDosya", { value: "olcum-2.csv", writable: true, enumerable: false, configurable: true, }); console.log(kayit.kaynakDosya); console.log(Object.keys(kayit).join(",")); console.log(Object.getOwnPropertyNames(kayit).join(",")); console.log(JSON.stringify(kayit)); console.log(JSON.stringify({ ...kayit })); console.log(JSON.stringify(Object.assign({}, kayit))); console.log("kaynakDosya" in kayit); console.log(Object.entries(kayit).length);
olcum-2.csv
sensor,deger
sensor,deger,kaynakDosya
{"sensor":"S-01","deger":21.4}
{"sensor":"S-01","deger":21.4}
{"sensor":"S-01","deger":21.4}
true
2
Özellik okunabilir durumdadır ve in işleci onu görür; ama Object.keys,
Object.entries, JSON.stringify, nesne yayma ve Object.assign onu atlar.
Object.getOwnPropertyNames ise sayılabilirliğe bakmaz, bu yüzden gizli kalan
özellikleri denetlemenin yolu odur.
Buradan pratik bir kural çıkar: veriye eşlik eden ama verinin parçası olmayan
bilgiler — kaynak dosya adı, iç sayaçlar, önbellek alanları — sayılamaz tanımlanırsa
dizileştirme ve kopyalama işlemlerine kendiliğinden karışmaz. Aynı sonucu simge (symbol)
anahtarlı özellikler de verir; onlar da Object.keys ve JSON.stringify çıktılarına
girmez.
Erişimci Özellikler
İkinci tanımlayıcı türünde value ve writable yerine get ve set fonksiyonları
bulunur. Böyle bir özelliğe erişimci (accessor) denir: okuma get fonksiyonunu,
yazma set fonksiyonunu çalıştırır.
Erişimcinin değeri, hesaplanan bir büyüklüğü özellik gibi sunmasıdır. Ölçüm kaydında okunan değer, ham değer ile kalibrasyon kaymasının toplamıdır; bunu bir yöntem yerine erişimci yapmak, çağıranı hesabın varlığından habersiz bırakır.
const olcumDavranisi = {}; Object.defineProperty(olcumDavranisi, "okunanDeger", { get() { return this.deger + this.kayma; }, set(yeniDeger) { if (typeof yeniDeger !== "number" || Number.isNaN(yeniDeger)) { throw new TypeError("okunan deger sayi olmalidir"); } this.deger = yeniDeger - this.kayma; }, enumerable: false, configurable: true, }); function olcumKaydi(sensor, deger, kayma) { const kayit = Object.create(olcumDavranisi); kayit.sensor = sensor; kayit.deger = deger; kayit.kayma = kayma; return kayit; } const kayit = olcumKaydi("S-01", 21.4, -0.4); console.log(kayit.okunanDeger); kayit.okunanDeger = 25; console.log(kayit.deger); console.log(kayit.okunanDeger); try { kayit.okunanDeger = "yirmi"; } catch (hata) { console.log(`${hata.constructor.name}: ${hata.message}`); } console.log(Object.hasOwn(kayit, "okunanDeger")); console.log(Object.hasOwn(olcumDavranisi, "okunanDeger")); console.log(JSON.stringify(kayit));
21
25.4
25
TypeError: okunan deger sayi olmalidir
false
true
{"sensor":"S-01","deger":25.4,"kayma":-0.4}
Erişimci prototipte tanımlıdır; kaydın kendi özelliği değildir. Buna karşın get ve
set gövdelerindeki this, erişimin yapıldığı nesneye — yani kaydın kendisine —
bağlanır. Zincirde bulunan bir fonksiyon, çağrıldığı nesnenin verisiyle çalışır; bu
kuralın tamamı sonraki konunun ilk dersinde ele alınacaktır.
Yazıcı fonksiyon, Programlama Temelleri kursundaki Nesneye Dayalı Programlamaya Giriş dersinde tanıtılan sınıf değişmezini korumanın doğrudan yoludur: geçersiz değer nesneye hiç girmez. Ayrıca erişimci sayılamaz tanımlandığı için dizileştirmeye girmez; son satırda yalnızca ham veri alanları görünür. Bu, hesaplanmış değerlerin depolanan veriyle karışmamasını sağlar.
Sınıf gövdesindeki get ve set sözcükleri bu tanımın kısaltmasıdır; ürettikleri
erişimci de prototype nesnesine, sayılamaz olarak yazılır.
Tanımlayıcıları Koruyan Kopyalama
Bir nesneyi kopyalamanın alışılmış yolları tanımlayıcıları taşımaz. Nesne yayma ve
Object.assign, kaynaktaki her özelliği okur ve hedefe sıradan bir veri özelliği
olarak yazar. Erişimciler bu işlemde donmuş değere dönüşür.
const kaynak = { sensor: "S-01", deger: 21.4, get etiket() { return `${this.sensor}/${this.deger}`; }, }; const yayilmis = { ...kaynak }; const tanimlaKopya = Object.defineProperties( Object.create(Object.getPrototypeOf(kaynak)), Object.getOwnPropertyDescriptors(kaynak), ); console.log(kaynak.etiket); console.log(yayilmis.etiket); console.log(tanimlaKopya.etiket); kaynak.deger = 30; yayilmis.deger = 30; tanimlaKopya.deger = 30; console.log(kaynak.etiket); console.log(yayilmis.etiket); console.log(tanimlaKopya.etiket); console.log(typeof Object.getOwnPropertyDescriptor(yayilmis, "etiket").get); console.log(typeof Object.getOwnPropertyDescriptor(tanimlaKopya, "etiket").get);
S-01/21.4 S-01/21.4 S-01/21.4 S-01/30 S-01/21.4 S-01/30 undefined function
Yayma ile üretilen kopyada etiket artık hesaplanmaz; kopyalama anındaki metni taşıyan
sabit bir veri özelliğidir, bu yüzden deger değiştiğinde eskisini göstermeye devam eder.
Object.getOwnPropertyDescriptors ile Object.defineProperties birlikte kullanıldığında
hem erişimciler hem de bayraklar korunur; Object.create çağrısı da prototip bağını
taşır. Kopyalamanın genel sorunu — hangi bilginin taşındığı, hangisinin düştüğü — kursun
son dersinde bütünüyle ele alınacaktır.
Özet
- Her özellik, değerinin yanında
writable,enumerable,configurablebayraklarını taşır;Object.getOwnPropertyDescriptorbunları okur. Object.definePropertyçağrısında belirtilmeyen bayraklar kapalı sayılır; atama ile üretilen özelliklerde ise üçü de açıktır.- Yazılamayan özelliğe atama katı kipte hata yükseltir, katı olmayan kipte sessizce
başarısız olur;
configurable: falsegeri alınamaz. - Sayılamaz özellikler
Object.keys,Object.entries,JSON.stringify, yayma veObject.assignsonuçlarına girmez;Object.getOwnPropertyNamesile görünür. - Erişimci özellikte okuma ve yazma birer fonksiyon çalıştırır; yazıcı, geçersiz değerin nesneye girmesini engelleyerek sınıf değişmezini korur.
- Yayma ve
Object.assigntanımlayıcıları taşımaz; erişimciyi sabit değere çevirir.
Sonraki Adım
writable: false tek bir özelliği kilitler. Bir nesnenin tamamının değişmesini önlemek,
ona yeni özellik eklenmesini yasaklamak ya da yalnızca var olan alanların değerini
değiştirilebilir bırakmak için özellik başına tanım yazmak zahmetlidir. Sonraki ders
nesnenin bütününe uygulanan dondurma ve mühürleme işlemlerini, bunların ne kadar derine
indiğini ve değişiklik gerektiğinde yeni nesne üretmenin kalıplarını ele alacak.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.