Ders 08 / 17
Ok Fonksiyonlarında Bağlam
Ok fonksiyonlarının kendi bağlamının olmaması, sözcüksel çözümleme, yapıcı ve prototip eksikliği, sınıf alanı olarak tanımlanan okların bedeli ve yanlış kullanım durumları.
İçindekiler
Önceki dersin son örneğinde geri çağrı sorununun üçüncü çözümü bir ok fonksiyonuydu ve
hiçbir bağlama işlemi gerektirmedi. Bunun nedeni, ok fonksiyonunun this değerini çağrı
yerinden almamasıdır.
Ok fonksiyonlarının kendilerine ait bir this bağlaması yoktur. Gövdelerinde geçen this,
sıradan bir değişken gibi çözümlenir: içeriden dışarıya doğru, tanımlandıkları kapsam
zincirinde aranır. Programlama Temelleri kursundaki Kapsam ve Yaşam Süresi dersinde
anlatılan sözcüksel kapsam kuralı burada this için de geçerlidir. Bu tek fark, önceki
dersteki dört kuralın hiçbirinin ok fonksiyonlarına uygulanmamasına yol açar.
Kendi Bağlamı Olmayan Fonksiyon
Ayrımı görmenin en kısa yolu, aynı nesnede iki tanım biçimini yan yana koymaktır.
const kayitYontemli = { sensor: "S-01", deger: 21.4, bicimle() { return `${this.sensor}: ${this.deger}`; }, }; const kayitOkla = { sensor: "S-02", deger: 19.8, bicimle: () => { return this === undefined ? "this: undefined" : "this: baska"; }, }; console.log(kayitYontemli.bicimle()); console.log(kayitOkla.bicimle()); const kopukOk = kayitOkla.bicimle; console.log(kopukOk()); console.log(kayitOkla.bicimle.call(kayitYontemli));
S-01: 21.4 this: undefined this: undefined this: undefined
Nesne değişmezi bir kapsam kurmaz. Ok fonksiyonu modül gövdesinde tanımlandığı için
this değeri oradan gelir ve modül gövdesinde bu değer undefined olur. Nesnenin
üzerinden çağrılması bunu değiştirmez.
Son satır kuralı kesinleştirir: call bile ok fonksiyonunun bağlamını değiştiremez.
Bağlanacak bir yer olmadığı için açık bağlanma sessizce etkisiz kalır — hata da vermez.
bind de aynı biçimde etkisizdir; yeni bir fonksiyon üretir ama içteki this
çözümlemesine dokunmaz.
Sözcüksel Çözümlemenin Yararı
Kendi bağlamının olmaması bir eksiklik gibi görünür; asıl değeri, bir yöntem içinden
verilen geri çağrılarda ortaya çıkar. Yöntemin this değeri, içeride tanımlanan ok
fonksiyonuna kendiliğinden taşınır.
class OlcumSerisi { constructor(sensor, degerler) { this.sensor = sensor; this.degerler = degerler; } etiketliOkla() { return this.degerler.map((deger) => `${this.sensor}:${deger}`).join(" "); } etiketliIsleve() { return this.degerler .map(function (deger) { return `${this.sensor}:${deger}`; }) .join(" "); } etiketliYedekle() { const kendisi = this; return this.degerler .map(function (deger) { return `${kendisi.sensor}:${deger}`; }) .join(" "); } } const seri = new OlcumSerisi("S-01", [21.4, 19.8]); console.log(seri.etiketliOkla()); try { console.log(seri.etiketliIsleve()); } catch (hata) { console.log(`islev ifadesi: ${hata.constructor.name}`); } console.log(seri.etiketliYedekle());
S-01:21.4 S-01:19.8 islev ifadesi: TypeError S-01:21.4 S-01:19.8
İkinci yöntemdeki işlev ifadesi kendi this bağlamasını kurar ve geri çağrı olarak
çağrıldığı için bu bağlama genel kurala düşer; katı kipte undefined olur ve okuma hata
yükseltir. Üçüncü yöntem, bağlamı sıradan bir değişkene kopyalar; ok fonksiyonları dile
girmeden önce yaygın olan çözüm budur ve hâlâ doğru çalışır. Birinci yöntem aynı işi ek
bir ad kullanmadan yapar.
Kural şu biçimde özetlenebilir: çağıran bir bağlam sağlamıyorsa ok fonksiyonu doğru seçimdir. Geri çağrılar, dizi yöntemlerine verilen işlevler ve bir yöntemin içinde tanımlanan yardımcı fonksiyonlar bu tanıma girer.
Eksik Olan Diğer Şeyler
Ok fonksiyonlarında olmayan tek şey this değil. arguments nesnesi, new.target ve
prototype özelliği de bulunmaz. Sonuncusu, ok fonksiyonlarının yapıcı olarak
kullanılamamasının doğrudan nedenidir.
const okKalibre = (deger) => deger * 1.02; function islevKalibre(deger) { return deger * 1.02; } console.log("prototype" in okKalibre); console.log("prototype" in islevKalibre); console.log(typeof okKalibre); try { new okKalibre(1); } catch (hata) { console.log(`ok ile new: ${hata.constructor.name}`); } function disIslev() { const icOk = () => arguments[0]; return icOk(); } console.log(disIslev("dis-arguman")); const kalanIle = (...argumanlar) => argumanlar.length; console.log(kalanIle(1, 2, 3)); console.log(okKalibre.name); console.log(okKalibre.length);
false true function ok ile new: TypeError dis-arguman 3 okKalibre 1
İkinci derste new işlecinin ilk adımı “prototipi fonksiyonun prototype nesnesine bağlı
boş bir nesne yaratmak” olarak yazılmıştı. Ok fonksiyonunda böyle bir nesne bulunmadığı
için o adım hiç uygulanamaz; hata bu eksikliğin sonucudur.
arguments de sözcüksel olarak çözümlenir: ok fonksiyonunun içinde kullanıldığında
kendisini saran işlevin argümanlarını verir. Bu davranışa güvenmek yerine kalan
parametreler kullanılmalıdır; hem ok fonksiyonlarında hem de işlevlerde aynı biçimde
çalışır ve gerçek bir dizi üretir. Son iki satır ok fonksiyonlarının ad ve parametre sayısı
gibi sıradan fonksiyon özelliklerini taşımaya devam ettiğini gösterir.
Sınıf Alanı Olarak Ok Fonksiyonu
Sınıf gövdesinde bir alanın ok fonksiyonuna atanması, bağlanma sorununu kesin biçimde çözer. Bedeli prototip modelinde ödenir.
class OlcumToplayici { constructor(sensor) { this.sensor = sensor; this.degerler = []; } ekle(deger) { this.degerler.push(deger); return this.degerler.length; } eklePratik = (deger) => { this.degerler.push(deger); return this.degerler.length; }; } const a = new OlcumToplayici("S-01"); const b = new OlcumToplayici("S-02"); console.log(Object.getOwnPropertyNames(OlcumToplayici.prototype).join(",")); console.log(Object.getOwnPropertyNames(a).join(",")); console.log(a.ekle === b.ekle); console.log(a.eklePratik === b.eklePratik); const kopukPratik = a.eklePratik; console.log(kopukPratik(21.4)); console.log(a.degerler.join(",")); console.log(a.eklePratik.call(b, 99)); console.log(a.degerler.join(",")); console.log(b.degerler.join(",") || "(bos)");
constructor,ekle eklePratik,sensor,degerler true false 1 21.4 2 21.4,99 (bos)
İlk iki satır ayrımı gösterir: ekle prototipte durur, eklePratik her örneğin kendi
özelliğidir. Üçüncü ve dördüncü satırlar sonucu ölçer — prototipteki yöntem tüm örneklerde
aynı fonksiyondur, ok alanı her örnekte ayrıdır.
Beşinci ve altıncı satırlar kazancı gösterir: nesnesinden koparılmış ok alanı doğru
toplayıcıya yazmaya devam eder. Son üç satır ise aynı özelliğin diğer yüzüdür — call ile
başka bir toplayıcıya yönlendirme denemesi etkisizdir; değer yine a nesnesine yazılır.
Ödünleşim nettir. Ok alanı bağlamı garanti eder ama örnek başına bir fonksiyon nesnesi
üretir, paylaşımı ortadan kaldırır ve yeniden yönlendirmeyi imkânsız kılar. Prototipteki
yöntem paylaşımlıdır ve yönlendirilebilir ama bağlamı çağrı yerine emanet eder. Önceki
dersteki yapıcıda bind kalıbı da aynı ödünleşimin bir başka yazımıdır.
Ok Fonksiyonunun Yanlış Olduğu Yerler
Bir kural olarak: bir fonksiyonun this üzerinden nesnesine erişmesi gerekiyorsa ok
fonksiyonu olamaz. Bu, nesne değişmezindeki yöntemleri ve prototipe yazılan davranışları
kapsar.
const olcumDavranisi = { bicimle: () => `${this?.sensor}`, }; const kayit = Object.create(olcumDavranisi); kayit.sensor = "S-01"; console.log(kayit.bicimle()); const dogruDavranis = { bicimle() { return `${this.sensor}`; }, }; const dogruKayit = Object.create(dogruDavranis); dogruKayit.sensor = "S-01"; console.log(dogruKayit.bicimle()); const sayacUreteci = () => { let sayi = 0; return { artir: () => (sayi += 1), oku: () => sayi, }; }; const sayac = sayacUreteci(); sayac.artir(); sayac.artir(); console.log(sayac.oku());
undefined S-01 2
İlk kayıt prototipini doğru biçimde almıştır, ama zincirdeki fonksiyon this değerini
zincirden değil kendi tanımlandığı kapsamdan okur; sonuç undefined olur. Yalnızca tanım
biçimi değiştirilerek doğru davranış elde edilir.
Son bölüm ise ok fonksiyonlarının doğru kullanıldığı ikinci kalıbı önceden gösterir:
nesnenin verisi this yerine kapatılmış bir değişkende tutulduğunda this hiç
gerekmez ve bağlanma sorunu ortadan kalkar. Bu kuruluşun adı kapanıştır ve sonraki dersin
konusudur.
Özet
- Ok fonksiyonlarının kendi
thisbağlaması yoktur; gövdedekithis, tanımlandıkları kapsamda sözcüksel olarak çözümlenir. call,applyvebindok fonksiyonunun bağlamını değiştiremez; sessizce etkisiz kalır.- Ok fonksiyonu bir yöntemin içinden verilen geri çağrılarda bağlamı kendiliğinden taşır; bağlamı çağıran sağlamıyorsa doğru seçimdir.
- Ok fonksiyonlarında
prototype,argumentsvenew.targetbulunmaz; bu yüzden yapıcı olarak kullanılamazlar. - Sınıf alanı olarak tanımlanan ok fonksiyonu örnek başına bir kopya üretir: bağlamı garanti eder, paylaşımı ve yönlendirilebilirliği kaybettirir.
- Nesne değişmezindeki yöntemler ve prototipe yazılan davranışlar ok fonksiyonu olamaz.
Sonraki Adım
Son örnekte sayaç, durumunu bir nesne özelliğinde değil, kendisini üreten fonksiyonun yerel değişkeninde tuttu. Fonksiyon döndükten sonra bile o değişken yaşamaya devam etti ve yalnızca döndürülen fonksiyonlar tarafından görülebildi. Sonraki ders bu yapıyı — kapanışları — ele alacak; kapatılan değişkenlerin yaşam süresini inceleyecek ve durum gizlemenin bu yolunu prototip zinciriyle karşılaştıracak.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.