IIFE Nedir, Ne Değildir?
Sınıf için üretilen js şöyle olabilirdi:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.add = function (point) {
return new Point(this.x + point.x, this.y + point.y);
};
Bunun sebebi Anlık-Çağrılan Fonksiyon İfadesi (Immediately-Invoked Function Expression) (IIFE) ile sarmalanmış olmasıdır, yani:
(function () {
// BODY
return Point;
})();
kalıtım ile yapar. TypeScript'in üst sınıfını _super
değişkeni olarak yakalamasına olanak tanır örnegin :
var Point3D = (function (_super) {
__extends(Point3D, _super);
function Point3D(x, y, z) {
_super.call(this, x, y);
this.z = z;
}
Point3D.prototype.add = function (point) {
var point2D = _super.prototype.add.call(this, point);
return new Point3D(point2D.x, point2D.y, this.z + point.z);
};
return Point3D;
})(Point);
Dikkat edin IIFE, TypeScript'in kolayca üst sınıf Point
içerisindeki _super
değişkenini yakalamasına izin verir ve sınıfın ana bloğunda sürekli olarak kullanılır.
__extends
Şunu fark edeceksiniz ki bir sınıftan türettiğinizde TypeScript ayrıca aşağıdaki fonksiyonu da oluşturur:
var __extends = this.__extends || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
__.prototype = b.prototype;
d.prototype = new __();
};
Burada d
türetilmiş sınıfı ve b
üst sınıfı ifade eder. Bu fonksiyon iki şey yapar:
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
yani üst sınıfının statik elemanlarını alt sınıfının üzerine kopyalard.prototype.__proto__ = b.prototype
yani etkin biçimde, alt sınıfının fonksiyonlarının prototiplerini isteğe bağlı olarak arama elemanlarına üst sınıfınınproto
'ları üzerinde atama yapar
İnsanlar nadiren 1'i anlamakta zorlanırlar, ancak birçok kişi 2 ile mücadele verir. Bu yüzden sırası ile açıklamaları.
d.prototype.__proto__ = b.prototype
Birçok insana bu konuyu anlattıktan sonra aşağıdaki açıklamanın en basiti olduğunu düşünüyorum. Öncelikle __extends
'deki kodun nasıl basit d.prototype.__proto__ = b.prototype
'a eşit olduğunu açıklayacağız, ve sonrasında kendi içindeki bu satırın neden önemli olduğunu. Bunu tamamen anlayabilmeniz için şunları bilmeniz gerekir:
__proto__
prototype
- çağrılan fonksiyon içerisinde
new
'ınthis
üzerindeki etkisi new
'ınprototype
ve__proto__
üzerindeki etkisi
JavaScript'teki tüm nesneler __proto__
elemanını içerir. Bu eleman eski tarayıcılar tarafından sıklıkla erişilemez durumdadır (bazen dökümantasyon bu sihirli özelliği [[prototype]]
olarak ifade eder). Bunun tek bir görevi vardır: Eğer arama sırasında nesne üzerinde özellik bulunamazsa (örneğin obj.property
) obj.__proto__.property
'e bakacaktır. Eğer hala bulunamadıysa ozaman obj.__proto__.__proto__.property
'a ta ki ikisi de: it is found veya the latest .__proto__
itself is null olana kadar. Bu JavaScript'in prototipsel kalıtım(prototypal inheritance) işlevselliğine neden destek verdiğini açıklar. Chrome konsolunda veya nodejs'te çalışan bir örneği şekilde gösterilmiştir:
var foo = {}
// foo.__proto__'da olduğu gibi foo üzerinde hazırlanışı
foo.bar = 123;
foo.__proto__.bar = 456;
console.log(foo.bar); // 123
delete foo.bar; // nesneden çıkar
console.log(foo.bar); // 456
delete foo.__proto__.bar; // foo.__proto__'dan çıkar
console.log(foo.bar); // undefined
__proto__
'yu anlıyorsun, harika. Başka bir yararlı bilgi ise, JavaScript içerisindeki tüm fonksiyon
'lar prototype
diye adlandırılan özelliğe sahiptir ve fonksiyonu geri işaret eden constructor
elemanına sahiptir. Aşağıda gösterilmiştir:
function Foo() { }
console.log(Foo.prototype); // {} yani zaten var ve tanımlanmamış
console.log(Foo.prototype.constructor === Foo); // `constructor` adında fonksiyonu geri işaret eden elemana sahip
Şimdi hadi çağrılan fonksiyon içerisinde new
'ın this
üzerindeki etkisi'ne bakalım. Aslında çağrılan fonksiyon içerisindeki this
, fonksiyondan geri dönecek yeni yaratılmış nesneyi işaret edecektir. Eğer fonksiyon içerisindeki this
üzerinde özelliği değiştirirseniz bunu görmek çok basittir:
function Foo() {
this.bar = 123;
}
// new operatörü ile çağrımı
var newFoo = new Foo();
console.log(newFoo.bar); // 123
Şimdi öğrenmeniz gereken son bir şey ise, fonksiyon üzerinde new
'ı çağırmak, fonksiyon çağrısından dönen yeni yaratılmış nesnenin __proto__
'su içindeki prototype
'ı kopyalar. İşte çalıştırıp tamamen anlayabilmeniz için gereken kod bu:
function Foo() { }
var foo = new Foo();
console.log(foo.__proto__ === Foo.prototype); // True!
İşte bu. Şimdi __extends
'e göz atın. Bu satırları numaralandırma özgürlüğünü kendimde buldum:
1 function __() { this.constructor = d; }
2 __.prototype = b.prototype;
3 d.prototype = new __();
Bu fonksiyonu satır 3 üzerindeki d.prototype = new __()
tersten okumanın etkili anlamı d.prototype = {__proto__ : __.prototype}
(new
'ın prototype
ve __proto__
üzerindeki etkisi yüzünden), önceki satırdaki (yani satır 2 __.prototype = b.prototype;
) ile birleştirme sonucunda d.prototype = {__proto__ : b.prototype}
alırsınız.
Ama durun, biz d.prototype.__proto__
istedik yani sadece prototip değişsin ve eski d.prototype.constructor
sürsün. Burası tam olarak ilk satırın öneminin işin içine girdiği yer (yani function __() { this.constructor = d; }
). Burada etkili şekilde d.prototype = {__proto__ : __.prototype, d.constructor = d}
'a (çağrılan fonksiyon içerisinde new
'ın this
üzerindeki etkisi yüzünden) sahip olacağız. Yani, d.prototype.constructor
'i eski haline getirdiğimizden beri, gerçekten değiştirdiğimiz tek şey __proto__
, bu nedenle d.prototype.__proto__ = b.prototype
.
d.prototype.__proto__ = b.prototype
önemi
Önemi şu ki, bu sizin fonksiyon elemanlarınızı alt sınıfa eklemenize ve diğerlerini üst sınıflardan almanıza izin verir. Bunun gösterimi aşağıdaki basit örnekte yapılmıştır:
function Animal() { }
Animal.prototype.walk = function () { console.log('walk') };
function Bird() { }
Bird.prototype.__proto__ = Animal.prototype;
Bird.prototype.fly = function () { console.log('fly') };
var bird = new Bird();
bird.walk();
bird.fly();
Aslında bird.fly
bird.__proto__.fly
'da aranacaktır (new
'ın bird.__proto__
'nun Bird.prototype
'u işaret ettireceğini hatırlayın) ve bird.walk
(türetilen eleman) bird.__proto__.__proto__.walk
'da aranacaktır (bird.__proto__ == Bird.prototype
ve bird.__proto__.__proto__
== Animal.prototype
gibi).