İçeriğe geç
academia.sh

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 this bağlaması yoktur; gövdedeki this, tanımlandıkları kapsamda sözcüksel olarak çözümlenir.
  • call, apply ve bind ok 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, arguments ve new.target bulunmaz; 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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat