Temel Javascript
Javascript öyle garip bir dil ki çoğu insan dilin en temel özelliklerini bilmeden onu kullanabilir. Çoğu zaman da öğrenmemiz gerekmez ancak bunları öğrenmeden de çok fazla ileri gidemeyiz. Bu yazıda javascript'in çok görünen ama bilinmeyen temellerine iniyoruz.
This
this javascript'te bir fonksiyonun çağrıldığı context'ini vermektedir. Eğer fonksiyon global
objeden çağrılmışsa this global objeyi gösterir. Tarayıcılarda bu global obje window
nesnesidir.
Tanımlandığı değil de çağrıldığı yer olarak üzerinde durmamım nedeni bunun farklı şeyler olmasıdır. Örneğin:
1var obj = {
2 arg1: 10,
3 f: function() {
4 console.log(this.arg1);
5 }
6};
7
8var obj2 = { arg1: 11 };
9
10obj.f(); // --> 10 yazar
11obj2.f = obj.f;
12obj2.f(); // --> 11 yazar
obj2.f = obj.f
diyerek f
fonksiyonunu bağlamından kopardık ve obj2
'ye setledik. obj2
üzerinden çağırdığımızda this artık obj
'yi değil obj2
'yi gösteriyordu. Yani this çağrıldığı yere göre dinamik olarak oluşan bir context'tir. Bu context'e (bağlama) this keyword'ü üzerinden erişiz.
Bu demek oluyor ki this'e çok güvenip bir şeyler yazarsanız bazı durumlarda bu problemlere yol açabilir.
1var obj = {
2 helperFunc: function() {
3 return "yardımcı kütüphane";
4 },
5 workerFunc: function() {
6 var utilValue = this.helperFunc();
7 }
8};
9
10var workFromHere = obj.workerFunc;
11workFromHere(); // --> TypeError: this.helperFunc is not a function
Bunun için bir çözüm var o da bind. Ama bu konuya sonra bakacağız.
Şimdi this ile ilgili başka bir keyword'e new'a balalım:
New
new constructor fonksiyonunu kullanmak içindir. Parametre geçirebilirsiniz veya geçirmeseniz de olur. Dönen obje artık yeni bir objedir. Her new yeni bir kopya oluşturur.
1function Triangle(name) {
2 this.name = name;
3}
4
5var triangle = new Triangle("Üçgen");
6var triangle2 = new Triangle("Üçgen2");
7console.log(triangle.name);
8console.log(triangle2.name);
Aynısını ES5 ile gelen Object.create
ile de yapabiliyoruz. Yani illa bir fonksiyon olmasına gerek yok.
1var shape = {
2 name: "Şekil"
3};
4
5var triangle = Object.create(shape);
6triangle.name = "Üçgen";
7
8console.log(shape.name);
9console.log(triangle.name);
Mesela şu iki objenin farklarına bir bakın:
1var myObj = Object.create(null, {name: { value: "Barış" }});
2console.log(myObj);
3var myObj2 = { name: "Barış" };
4// ya da:
5// var myObj2 = Object.create({ name: "Barış" });
6console.log(myObj2);
7
8console.log(myObj instanceof(Object));
9console.log(myObj2 instanceof(Object));
Function değil Object yaratırken Object.create'de prototype'ı karıştırmayın, prototype daha çok Function'larda kullanılır.
create metodunun ilk parametresini null
vererek aslında Object'ten türememesi sağladık. Bunun tabii ki ilginç sonuçları var. Bir nesne Object'ten türemediğinde mesela şunu yapamıyoruz:
1for(var x in myObj) {
2 console.log(x);
3}
Object.create
ile birlikte ilginç iki özellik daha kullanabiliyoruz, bunlardan biri property, diğeri de önceden tanıdığımız ve sevdiğimiz prototype.
Property
Yeni yaratılan bir obje için:
Object.create(proto[, propertiesObject])
Zaten var olan bir objeye property eklemek için:
Object.defineProperty(obj, prop, descriptor)
Property'nin özellikleri:
- value: property'nin değeri
- writable: (varsayılan false) false ise o property'ye setleme yapılamaz (immutable)
- enumerable: (varsayılan false) false ise Object.keys'te veya for..in'de kullanılamaz
- configurable: (varsayılan false) false ise property'nin bu özellikleri sonradan değiştirilemez veya property silinemez
- özel set: property'ye atama verilen fonksiyon üzerinden yapılır
- özet get: property'nin değeri verilen fonksiyon üzerinden döndürülür.
Define property ile ilgili örnekler: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
Object.create ile birlikte kullanımı: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Using_propertiesObject_argument_with_Object.create()
Prototype
Prototype chain: If you try to look up a key on an object and it is not found, JavaScript will look for it in the prototype. It will follow the "prototype chain" until it sees a null value. In that case, it returns undefined
1function Shape(name) {
2 this.name = name;
3}
4
5Shape.prototype.area = function () {
6 return this.edges[0] * this.edges[1] / 2;
7}
8
9function Triangle(name) {
10 Shape.call(this, name);
11 this.edges = [3, 4, 5];
12}
13
14// Burada object create diyerek yeni bir obje yaratılmış oluyor.
15// Kullanmasaydık Triangle prototype ile Shape prototype aynı nesneyi gösterecekti
16// Bu yüzden Triangle'a özel prototype fonksiyonları tanımlayamayacaktık (Otomatik Shape'e de tanımlanırdı)
17Triangle.prototype = Object.create(Shape.prototype);
18Triangle.prototype.constructor = Triangle;
19
20var triangle = new Triangle("Dik üçgen");
21console.log(triangle.name);
22console.log(triangle.area());
Ne bu? Aa object oriented. Ama prototype-based. Her ne kadar ES6'da class keyword'ü gelse de (syntax olarak değişse bile temelde yapı aynı) Javascript'in diğer OO dillerindeki class
gibi bir yapısı olmadığından obje üzerinden inheritance sağlar.
Burada this.edges
nasıl çalıştı diye soracak olursak; Triangle prototype ile Shape'den türediği için aslında Shape.prototype.area
Triangle context'iyle çalıştı.
Bu arada context'i çok andım şuna link vermezsem olmaz.
1...
2Shape.call(this, name);
3...
4console.log(triangle.name);
Burada da Shape'in constructor'ını Triangle'ın this'ini geçirerek çağırdık ve Shape içindeki this'e olan atama aslında Triangle'a olmuş oldu. Ve bu bize yepyeni konuları açtı: call, apply, ve bind
Call ve Apply
Eğer call'u anlarsak apply'ı da anlayacağız, ikisi de aynı şey aslında.
fun.call(thisArg[, arg1[, arg2[, ...]]])
fun.apply(thisArg, [argsArray])
İkisinde de ilk parametre thisArg, call'da diğer parametreleri sırayla geçirirken apply'da kalan parametreler bir array içinde geçirilebiliyor. Uygun durumlarda bunlardan birini seçip kullanabilirsiniz. Örneğin prototype hiyerarşisini sağlayabilmek için parent/üst constructor'ı call ile çağırdık. Bu this argümanını geçirebilmek için gereklidir.
Peki apply ne işe yarayacak. apply benim gördüğüm kadarıyla javascript framework'ü yazanlar için oldukça kullanışlı. Özellikle arguments değişkeniyle birlikte kullanıldığında. Örneğin aşağıdaki koddaki loggerFunc varolan herhangibir fonksiyonun parametrelerini ve sonucunu yazdırır.
1var testFunc = function(arg1, arg2, arg3) {
2 return arg1 + arg2 + arg3;
3};
4
5loggerFunc = function(originalFunc) {
6 return function() {
7 // fonksiyon parametrelerini yazdır
8 console.log(arguments);
9
10 // orijinal fonksiyonu çalıştır
11 var result = originalFunc.apply(this, arguments);
12
13 // sonucu yazdır
14 console.log(result);
15
16 // sonucu döndür
17 return result;
18 };
19}
20
21var wrapped = loggerFunc(testFunc);
22wrapped(3, 4, 5);
Bind
fun.bind(thisArg[, arg1[, arg2[, ...]]])
Bind ise yine apply ve call gibi parametrelerin setlenmesini sağlar ancak bu sefer fonksiyonu direkt çalıştırmaz, yerine; verilen parametrelerin hazır setli bir versiyonunu yeniden üretir. bind'dan dönen değer yine bir fonksiyondur ve siz istediğiniz zaman çağırabilirsiniz. Çağırdığınızda, önceden setlediğiniz parametrelerle çalışacaktır.
1function test(arg1, arg2, arg3) {
2 this.arg1 = arg1;
3 this.arg2 = arg2;
4 console.log(arg3);
5}
6
7var results {};
8var testWithBind = test.bind(results, 1, 2);
9testWithBind(3); // 3
10console.log(results); // 1, 2
Gördüğünüz gibi this yerine results objesini geçirdim, böylece arg1 ve arg2 results objesine setlendi. Aynı zamanda bind ile 1 ve 2 parametrelerini de setledim. İlk iki parametre artık verdiğim değerlere bağlı bir şekilde çalışacak. O yüzden testWithBind fonksiyonunu tek parametreyle çağırdığım halde bu arg3 parametresine denk geldi.
Eğer eski tarayıcı sürümlerine bağımlılığınız varsa (müşterilerinizin bir kısmı IE 7/8 kullanıyor olabilir, şu siteden kullanacağınız özelliği kontrol etmekte ve gerektiği durumlarda polyfill kullanmakta yarar var. http://kangax.github.io/compat-table/es5/ (Show obselete browsers seçeneğini seçin)
Denk geldikçe bu yazıya javascript'le ilgili bilinmesi gereken temel konuları ekleyeceğim.