Javascript ve Nesnel Programlama

06/2012

Bu yazıyı bitmiş bir makale değil de benim tuttuğum notlardan oluşan bir yazı olarak düşünmek daha doğru. O yüzden bölük bölçük ve bitmemiş durumda zamanla düzelte düzelte adam edeceğim.

Baya bir süredir Javascript kullanırım ancak bu kullanım var olan JQuery pluginlerini doğru dürüst entegre etmek, gerekirse bir iki fonksiyon yazmaktan öteye geçmez. Son bir iş var yapmam gereken ve pek öyle basit bir iş değil adam akıllı bir MVC yapısı kurmam, gerekli. Önce Backbone.js kullanmayı denesem de, Backbone'un view katmanının DOM elemanlarını manuple edecek şekilde hazırlanmış olması işi bozdu. Zira benim yapacağım iş kullanıcıya canvas üzerinde bir takım resimler çizdirmek.

Javascript ile Nesne Yönelimli Programlamaya dair tuttuğum notlar şu şekilde

Variable Scope (Değişken Kapsamı)

Javascript değişkenlerin nerede görünür nerede görünmez olduğuna function scope (fonksiyon kapsam) kullanarak karar veriyor. Yani her değişken kendi içinde bulunduğu fonksiyon içerisinde görünür durumda. Ancak iç içe geçmiş fonksiyonlarda  içerideki bir değişken kullanıldığı zaman önce fonksiyon içerisinde, eğer bulunamaz ise dıştaki fonksiyonda aratılıyor.

Aşağıdaki örnekte bir myvar adlı bir değişken var, bu global bir değer en tepede duruyor. is_exists() fonksiyonu çağırıldığı zaman önce fonksiyon içerisinde aranıyor, bulunamadığı için bir üste yani global katmanına çıkılıyor. Orada bulunduğu için myvar görüntüleyebiliyoruz.

var myvar = "here i am!";
function is_exists() { console.log(myvar); };
is_exists();
> here i am!

Global değişken görülmüyor olsa da eğer fonksiyonları iç içe kullanır isek, alttaki fonksiyon üsttekine ait değişkenleri görebiliyor. Aşağıdaki örnekte outer fonksiyonunu çağırıyoruz, o da değer olarak kendi içindeki inner fonksiyonunu döndürüyor. inner fonksiyonu outer'a ait myvar değerini getiriyor.

function outer()  {
    var myvar = "here i am!"
        function inner() {
            console.log(myvar);
        }
    return inner();
};
outer();
> "here i am!"

Fonksiyon Tanımlamaları

Javascript'te klasik fonksiyon tanımlamaları mümkün, bunun yanında bir de anonim (isimsiz) fonksiyonlar var. Anonim fonksiyonlar genelde bir değer üzerine atanarak kullanılıyor ya da başka fonksiyonlara parametre olarak gönderiliyor. (evet biraz kafa karıştırıcı olmaya başladı)

Aşağıdaki bir klasik fonksiyon oluşturma örneği:

function nameOfFunction(listOfVariableNames) {
    ...
    ...
}
var a = nameOfFunction();

Bu fonksiyonun yaptığı işlemin sonucunu bir a değişkenine atadık. Eğer istersek tekrar kullanabilir, başka bir değişkene de atayabiliriz.

Aşağıdaki ise isimsiz bir fonksiyon

b = function(listOfVariableNames) {
    ...
    ...
};

Burada fonksiyonumuzu b değişkenine atamış oluyoruz ancak atamayabilirdik. Bir fonksiyon diğerine parametre olarak verildiğinde kullanılıyor genelde bu özellik.

Nesneler

Javascript her ne kadar kendini nesnel bir programlama dili olarak tanıtsa da Javascript'de her şey nesne değil. İlkel değerlere (undefined, null, boolean, string ve number) sahip. İlginçtir ki bu ilkel değerlere objeymiş gibi davranır, her hangi bir metodunu çağırmaya kalkarsanız; ilkel değer, nesneye düşüyor (fall back) ve metod çağırılıyor.

var a = "hi!" // bu bir string, yani ilkel değer
console.log(a.length); // burada fall back işlemi gerçekleşiyor, objeye dönüşüyor
> 3

İkinci bir gariplik ise Javascript'de sınıf kavramının bulunmayışı. O_o

Ancak türetme işlerini bir obje oluşturup buna ait metodları ve özellikleri yine de başka objelere aktararak halledebiliyoruz.

Javascript'de bir obje oluşturmanın en kısa yolu şu şekilde:

var ninja = {
    name: 'Shadow Warrior'
    health: 100,
    swing_sword: function() {
        alert("swosh!");
    }
};
ninja.swing_sword();

ninja adında bir obje oluşturmuş olduk. İçindeki swing_sword (kılıç salla) metodunu kullandık. Ancak bunu oluştururken dict nesnesini baz alarak oluşturduğumuz için her ne kadar boş bir nesne oluşturduk gibi gözükse de dict objesinin metodları ile birlikte gelmiş oluyor.

İnşa Edici Fonksiyonlar (Constructor Functions) ve New ifadesinin kullanımı.

Adından da anlaşılacağı üzer inşa edici fonksiyonlar sizin nesneleri inşa etmenizi sağlayan fonksiyonlar oluyor. Bizim nesneleri dinamik bir biçimde inşa edebilmemizi sağlıyor. Örneğin bir kaç ninja nesnesine ihtiyaç duymuş olalım ve bunların name ve health değerleri birbirinden farklı olsun. Bu durumda uzun uzun yazmak yerine aşağıdaki gibi bir yol izleyebiliriz.

function NinjaConstructor(name, health) {
    this.name = name; 
    this.health = health;
    this.swing_sword: function() {
        alert("swosh!");
    }
}
var ninja1 = new NinjaConstructor('mirat', 100);
var ninja2 = new NinjaConstructor('osman', 62);
console.log(ninja1.name, ninja1.health);
> mirat, 100
console.log(ninja2.name, ninja2.health);
> osman, 62

Burada new ifadesi dikkatinizi çekmiştir, bu ifade al bu fonksiyondan obje üret demek için kullanılıyor. new ifadesini kullanmasaydık atadığınız değerler fonksiyonun kendisi olacaktı.

İnşa edici fonksiyonumuzu (NinjaConstructor) yazdıktan sonra aslında fonksiyonun ürettiği özellik ve metodları üretilen nesnelere aktardığımız için bir nebze miras alma-verme mevzusunu çözmüş oluyoruz. Ancak yine de bazı durumlarda bu yeterli olmaya biliyor.

Miras Mevzusu (Prototypal Inheritance)

Javascript miras alma işlerini nesnelerin içine yerleştirdiği prototype adındaki başka bir objeyi kullanarak hallediyor. Siz nesne içindeki bir özelliği ya da metodu çağırdığınızda eğer yok ise, prototipine bakılıyor, orada da yok ise undefined döndürüyor. Şöyle,

// bu a nesnesini, prototip olarak kullanacağım:
class a = {
    hello: function () {
        alert("hello!")
    },
}

// bu da miras alacak şahıs:
class b = {
    bye: function () {
        alert("goodbye!")
    },
}

// şu anda b nesnesi hello fonksiyonuna sahip değil
console.log(b.hello)
> undefined

// b nesnesinin prototipini a olarak belirliyorum
b.__proto__ = a

// hello fonksiyonunu çağırdığımda önce b objesine bakılacak
// bulunamadığı için prototipine gidilecek, oradaki metod çalıştırılacak.
b.hello()
> hello!

Bu arada yeri gelmişken hemen belirtmek gerekir ki new ifadesi ise aslında yeni bir obje oluştururken, ona verdiğiniz inşa edici fonksiyonun prototipine bakıyor.