Ders 06 / 17
this Bağlanması
Bağlamın çağrı yerinde belirlenmesi; genel, örtülü, açık ve yapıcı bağlanma kuralları, aralarındaki öncelik sırası ve yöntemin nesnesinden kopması.
İçindekiler
Prototip Modeli konusu boyunca yöntemler this üzerinden çalıştı ve bu, sanki fonksiyonun
tanımlandığı nesneyi bildiği izlenimini verdi. İzlenim yanlıştır. Önceki derste
Object.assign(kopya, this, ...) çağrısında olduğu gibi, prototipteki bir fonksiyon
zincirinde hiç bulunmadığı bir nesne üzerinde de çalışabilir.
Kural şudur: this değeri fonksiyonun tanımlandığı yere değil, çağrıldığı biçime
bakılarak belirlenir. Aynı fonksiyon, dört farklı çağrı biçiminde dört farklı değere
bağlanır. Bu ders dördünü ayırt eder ve aralarındaki öncelik sırasını kurar.
Genel Bağlanma
En yalın çağrı biçimi, fonksiyonun hiçbir nesneye iliştirilmeden çağrılmasıdır. Katı kipte
this değeri undefined olur.
function bagiOku() { return this; } console.log(bagiOku() === undefined); console.log(typeof globalThis); const katiOlmayanBagiOku = new Function("return this;"); console.log(katiOlmayanBagiOku() === globalThis); console.log(katiOlmayanBagiOku() === undefined);
true object true false
Katı olmayan kipte aynı çağrı this değerini genel nesneye bağlar; globalThis bu
nesnenin standart adıdır. Fark önemlidir, çünkü yanlışlıkla nesnesiz çağrılan bir yöntem
katı kipte hemen hata verirken, katı olmayan kipte genel nesneye sessizce özellik yazar.
Modül dosyaları ve sınıf gövdeleri katı kipte çalıştığı için, ölçüm kaydı örneklerinin
tamamında geçerli olan davranış birincisidir.
Örtülü Bağlanma
Fonksiyon bir nesnenin özelliği üzerinden çağrılırsa this, o nesneye bağlanır. Belirleyen
şey çağrı yerindeki nokta işlecinin solundaki nesnedir.
const kayit = { sensor: "S-01", deger: 21.4, zaman: 1000, bicimle() { return `${this.sensor}@${this.zaman}: ${this.deger}`; }, }; console.log(kayit.bicimle()); const kopukBicimle = kayit.bicimle; try { console.log(kopukBicimle()); } catch (hata) { console.log(`kopuk cagri: ${hata.constructor.name}`); } const kayitlar = [kayit]; try { console.log(kayitlar.map(kayit.bicimle)[0]); } catch (hata) { console.log(`geri cagri: ${hata.constructor.name}`); } console.log(kayitlar.map((k) => k.bicimle())[0]); const olcum = { sensor: "S-DIS", kaynak: { sensor: "S-IC", bicimle() { return `bag: ${this.sensor}`; }, }, }; console.log(olcum.kaynak.bicimle());
S-01@1000: 21.4 kopuk cagri: TypeError geri cagri: TypeError S-01@1000: 21.4 bag: S-IC
İkinci ve üçüncü satırlar dilin en sık karşılaşılan tuzağını gösterir. kayit.bicimle
ifadesi bir fonksiyon değeri verir; nesneyle bağı bu değerin içinde taşınmaz. Değer
bir değişkene atandığında ya da geri çağrı olarak aktarıldığında çağrı yeri değişir ve
bağlanma genel kurala düşer. Katı kipte this undefined olduğu için this.sensor
okuması hata yükseltir.
Bu, Programlama Temelleri kursundaki Değer ve Referans ile Aktarım dersindeki gözlemin bir sonucudur: aktarılan şey fonksiyonun kendisidir, bulunduğu bağlam değil. Dördüncü satırdaki çözüm en dolaysızıdır — çağrıyı nesne üzerinden yapan bir sarmalayıcı yazmak.
Son satır zincir uzunluğunun rol oynamadığını gösterir: yalnızca son halka sayılır.
olcum.kaynak.bicimle() çağrısında this, kaynak nesnesidir; dıştaki olcum hiç
dikkate alınmaz.
Açık Bağlanma
call, apply ve bind işlemleri bağlamı çağrı yerinde açıkça belirtir. Ayrıntıları
sonraki dersin konusudur; burada yalnızca kuralın bir üçüncü biçim olduğu gösterilir.
const olcumDavranisi = { bicimle() { return `${this.sensor}@${this.zaman}: ${this.deger}`; }, }; const zincirdeki = Object.create(olcumDavranisi); zincirdeki.sensor = "S-01"; zincirdeki.deger = 21.4; zincirdeki.zaman = 1000; const yabanci = { sensor: "S-99", deger: 5.5, zaman: 7 }; console.log(zincirdeki.bicimle()); console.log(olcumDavranisi.bicimle.call(yabanci)); console.log(zincirdeki.bicimle.call(yabanci)); console.log(Object.getPrototypeOf(yabanci) === olcumDavranisi);
S-01@1000: 21.4 S-99@7: 5.5 S-99@7: 5.5 false
Son satır dersin başındaki savı doğrular: yabanci nesnesinin zincirinde ölçüm davranışı
yoktur, buna karşın aynı fonksiyon onun verisiyle sorunsuz çalışır. Prototip zinciri
fonksiyonun nerede bulunacağını belirler; this ise hangi veri üzerinde
çalışacağını. İkisi birbirinden bağımsızdır.
Yapıcı Bağlanma
new işleci, ikinci derste sıralanan adımların ikincisinde this değerini yeni yaratılan
nesneye bağlar.
function OlcumKaydi(sensor, deger) { this.sensor = sensor; this.deger = deger; } OlcumKaydi.prototype.bicimle = function () { return `${this.sensor}: ${this.deger}`; }; const yapiciyla = new OlcumKaydi("S-01", 21.4); console.log(yapiciyla.bicimle()); console.log(Object.getPrototypeOf(yapiciyla) === OlcumKaydi.prototype); try { OlcumKaydi("S-02", 19.8); } catch (hata) { console.log(`new olmadan: ${hata.constructor.name}`); } class SinifKaydi { constructor(sensor) { this.sensor = sensor; } bicimle() { return `sinif: ${this.sensor}`; } } const ornek = new SinifKaydi("S-03"); console.log(ornek.bicimle()); const kopuk = ornek.bicimle; try { kopuk(); } catch (hata) { console.log(`sinif yontemi kopuk: ${hata.constructor.name}`); }
S-01: 21.4 true new olmadan: TypeError sinif: S-03 sinif yontemi kopuk: TypeError
Üçüncü satır, yapıcı işlevin new olmadan çağrılmasının katı kipte nasıl sonuçlandığını
gösterir: this undefined olduğu için ilk atama hata yükseltir. Sınıf sözdiziminin bu
çağrıyı baştan yasaklaması, tam da bu sessiz hatayı önlemek içindir.
Son satır önemli bir noktayı yineler: sınıf sözdizimi kopma sorununu çözmez. Sınıf yöntemleri de prototipte duran sıradan fonksiyonlardır ve nesnelerinden ayrıldıklarında aynı biçimde bağlarını yitirirler.
Geri Çağırmada Bağlam Kaybı
Dört kural, hataların çıktığı yeri de açıklar. Bir yöntem, gövdesinde başka bir işleve fonksiyon geçirdiğinde o fonksiyon ayrı bir çağrı olur; dış yöntemin bağlamı ona taşınmaz.
const toplayici = { birim: "C", olcumler: [21.4, 22.8, 20.1], bicimliListe() { return this.olcumler.map(function (deger) { return `${deger}${this.birim}`; }); }, bicimliListeThisArg() { return this.olcumler.map(function (deger) { return `${deger}${this.birim}`; }, this); }, }; try { console.log(toplayici.bicimliListe()); } catch (hata) { console.log("geri çağırmada:", hata.constructor.name, "-", hata.message); } console.log(toplayici.bicimliListeThisArg());
geri çağırmada: TypeError - Cannot read properties of undefined (reading 'birim') [ '21.4C', '22.8C', '20.1C' ]
Birinci çağrıda geri çağırma işlevi genel bağlanmaya düşer; katı kipte this tanımsız
olduğu için özellik okuması hata verir. Dizi yöntemlerinin bir bölümü ikinci bir
parametre alır ve geri çağırmayı verilen nesneye bağlar; ikinci çağrı bunu kullanır.
Bu parametre her arayüzde bulunmaz. Genel çözüm iki adımdan birindedir: bağlamı açık
bağlanmayla sabitlemek (sonraki ders) ya da kendi this bağını kurmayan bir fonksiyon
biçimi kullanmak (üçüncü ders). Sorunun kaynağı, çağrı yerinin fonksiyonun tanımlandığı
yerden bağımsız olmasıdır.
Öncelik Sırası
Birden fazla kural aynı anda geçerli göründüğünde hangisinin kazandığı sabittir: yapıcı bağlanma, açık bağlanmayı; açık bağlanma, örtülü bağlanmayı; örtülü bağlanma, genel bağlanmayı geçersiz kılar.
function sensoruYaz(sensor) { this.sensor = sensor; return this; } const hedef = { sensor: "baslangic" }; const digerHedef = { sensor: "diger" }; const bagli = sensoruYaz.bind(hedef); bagli("acik-bagli"); console.log(hedef.sensor); digerHedef.calistir = bagli; digerHedef.calistir("ortulu-denemesi"); console.log(hedef.sensor); console.log(digerHedef.sensor); const uretilen = new bagli("yapici"); console.log(uretilen.sensor); console.log(hedef.sensor); console.log(Object.getPrototypeOf(uretilen) === sensoruYaz.prototype); const nesne = { sensor: "ortulu", oku() { return this.sensor; }, }; console.log(nesne.oku()); console.log(nesne.oku.call(digerHedef));
acik-bagli ortulu-denemesi diger yapici ortulu-denemesi true ortulu diger
İkinci ve üçüncü satırlar açık bağlanmanın örtülü bağlanmayı yendiğini söyler: bağlı
fonksiyon bir nesnenin özelliği olarak çağrılsa bile yazma işlemi hedef üzerinde
gerçekleşir, digerHedef değişmez. Dördüncü ve beşinci satırlar yapıcı bağlanmanın açık
bağlanmayı yendiğini gösterir: new ile çağrıldığında bağlı hedef göz ardı edilir, yeni
nesne kullanılır ve zincir yapıcının prototype nesnesine kurulur.
Sıra tek cümleyle özetlenebilir: new > bind/call/apply > nokta işleci > hiçbiri.
Beşinci bir durum daha vardır — hiçbir kurala uymayan, kendi this değeri olmayan
fonksiyonlar — ve iki ders sonra ele alınacaktır.
Özet
thisdeğeri fonksiyonun tanımlandığı yere değil, çağrıldığı biçime bakılarak belirlenir.- Genel bağlanmada
this, katı kipteundefined, katı olmayan kipte genel nesnedir. - Örtülü bağlanmada nokta işlecinin solundaki nesne bağlanır; yalnızca son halka sayılır.
- Yöntem bir değişkene atandığında ya da geri çağrı olarak aktarıldığında bağı kopar; sınıf yöntemleri bu davranışta ayrıcalıklı değildir.
- Prototip zinciri fonksiyonun nerede bulunacağını,
thishangi veri üzerinde çalışacağını belirler; ikisi bağımsızdır. - Öncelik sırası: yapıcı bağlanma, açık bağlanma, örtülü bağlanma, genel bağlanma.
Sonraki Adım
Bu derste açık bağlanma yalnızca bir kural olarak tanıtıldı; onu kuran üç işlemin
farkları ele alınmadı. call ile apply argümanları nasıl aktarır, bind neden kalıcı
bir bağ üretir ve bu işlemler bir nesnenin zincirinde olmayan bir yöntemi ona nasıl
ödünç verir? Sonraki ders bu üç işlemi ve fonksiyon ödünç alma kalıbını inceleyecek.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.