JavaScript Temelleri

Bölüm 7

Function Nesne Sınıfı

Bölüm 7 Sayfa 1

7.1 - Function Nesne Sınfının Tanıtımı

Fonksiyonlarla çalışmak konusu bölüm 2.5 de geniş ölçüde incelenmişti. Burada, fonksiyon nesne sınfı incelenecektir.

7.2 - Function Nesne Sınıfı Örnekleri Yapılandırıcısı

Function nesne sınıfı örnekleri yapılandırıcısı, öntanımlı Function() fonksiyonudur. Bu fonksiyon, new bildirimi ile birlikte kullanıldığında, yeni Function nesne sınıfı örneklerinin yapılandırılması işlevini görür. Kullanımı,

d = new Function(arg1, arg2, ..., arg n, fonksiyon gövdesi);
		

şeklindedir. Tüm bu parametreler karakter digisi tipinde ve isteğe bağlıdır. Eğer istenirse, fonksiyon gövdesi olmadan bir Function nesne sınıfı örneği yaratılabilir, fakat doğal olarak böyle bir fonksiyonun bir işlevi olmayacaktır. Eğer tek bir argüman belirtilmişse, bu argüman fonksiyon gövdesi olarak kabul edilir.

Bir Function nesne sınıfı örneğinin gövdesi, çözümlenebilir bir yazılımda olmalıdır aksi halde, SyntaxError istisnası fırlatılır. Bu hata, birçok belge çözümleyicide, programı bir uyarı mesajı ile durdurur.

Eğer, birden çok argüman kullanılmışsa, en sonuncusu fonksiyon gövdesi, öndan öncekiler, "Formal Parametre Listesi" olarak kabul edilir.

Formal parametre listesinin tümü aynı karakter dizgisine toplandığından, yazılım biçimi önemli değildir. Bu düşünce ile,

var k=new Function("a","b","c","return (a+b)*c;");

ile,

var k=new Function("a,b","c","return (a+b)*c;");
		

aynı sonucu verir.

Function nesne sınıfı yapılandırıcısı ile oluşturulan Function nesne sınıfı örneklerinin fonksiyon adları yoktur. Bu nedenle, bu tip fonksiyonlara, anonim (isimsiz) fonksiyonlar adı verilir. Bu fonksiyonlar, fonksion literalleri ve fonksiyon bildirimi ile oluşturulmuş fonksiyonlardan ayırdedilmek üzere fonksiyon nesneleri (function objects) olarak da adlandırılırlar. Aşağıda, bir uygulama sayfasında çalışan bu konudaki bir JavaScript programı ve sonucu görülmektedir:

...

var topla = new Function('x', 'y', 'var t = x + y; return t;'); 
sonuçYaz('2 + 2  = ', topla(2, 2), 'b7.2.1-uyg-1-sonuç-1');
...
		

Program Sonucu :

Fonksiyon yapılandırıcısının kullanımı ile fonksiyon nesne sınıfı örnekleri yaratılmasının hiçbir gereği yoktur. Bu tür fonksiyon sınıfı örneği her çağılışta yeniden çözümlenmek (parsing) zorundadır ve bu da özellikle döngü içlerinde önemli bir performans kaybına neden olur. Bunun yerine sadece yaratıldığında çözümlenen fonksiyon literalleri kullanılabilir.

7.3 - Function Nesne Sınıfı Örnekleri Yapılandırıcısının Özellikleri

Fonksiyon nesne sınıfı yapılandırıcı fonksiyonunun içsel [[Prototype]] özelliğinin değeri, öntanımlı Fonksiyon nesne sınıfı prototip nesnesidir. Fonksiyon nesne sınıfı yapılandırıcı fonksiyonun sadece bir tek prototype özelliği vardır.

7.3.1 - Function Nesne Sınıfı Örnekleri Yapılandırıcısının prototype Özelliği

Fonksiyon nesne sınıfı yapılandırıcı fonksiyonunun prototype özelliğinin başlangıç değeri, öntanımlı fonksiyon prototip nesnesidir

7.4 - Function Nesne Sınıfı Prototip nesnesinin Özellikleri

Fonksiyon nesne sınıfı prototip nesnesine,

Function.prototype
		

şeklinde erişilir. Bu nesnesinin değeri,

Function.prototype = fonksiyon nesne sınıfı prototip nesnesi (boş bir fonksiyon nesnesi)
		

dir.

Fonksiyon nesne sınıfı prototip nesnesi, yine bir fonksiyon nesnesidir. Bu nesne boş bir fonksiyondur ve istendiği kadar argüman ile çağrıldığında geriye undefined değeri döndürür.

Fonksiyon nesne sınıfı prototip nesnesinin içsel [[Prototype]] özelliğinin değeri öntanımlı Object prototip nesnesidir. Bu da, fonksion nesne sınıfının, generik nesne sınıfının bir alt sınıfı olduğunu açıklar.

Fonksiyon nesne sınıfı prototip nesnesinin kendisine özgü bir valueOf() metodu yoktur. Fakat generik nesne sınıfı prototipinin valueOf() metodunu kalıtımla elde eder.

7.4.1 - Function Nesne Sınıfı Prototip Nesnesinin constructor Özelliği

Herhangibir nesne sınıfı prototip nesnesinin başlangıç yapısı, o nesne sınıfının sınıf nesnesinin başlangıçtali yapısıdır. Herhangibir nesne sınıfı prototip nesnesinin constructor özellğinin değeri, o nesne sınıfının yapılandırıcı fonksiyonunun yazılımıdır. Bu bilgilerle, fonksiyon nesne sınıfı prototip nesnesinin constructor özelliğinin değeri, öntanımlı Function() yapılandırıcı fonksiyonu olacaktır. Bunu bir JavaScript programı ile kontrol edebiliriz. Bu programın bir uygulama sayfasında verdiği sonuçlar aşağıda görülmektedir:

...
sonuçYaz('Function.constructor = ', Function.constructor, 'b7.4.1-uyg-1-sonuç-1');
...
		

Program Sonucu :

Uygulama sayfasında görüntülenen sonuçtan, fonksiyon nesne sınıfı prototip nesnesinin yapılandırıcısının beklendiği gibi, Function() öntanımlı yapılandırıcı fonksiyonu olduğu görülmektedir. Bu bir öntanımlı fonksiyon olduğundan yazılımı kullanıcılara kapalıdır. Bu nedenle, {native code} (öntanımlı kod) sonucu görüntülenmektedir.

7.4.2 - Function Nesne Sınıfı Prototip Nesnesinin toString() Metodu

Fonksiyon nesne sınıfının toString() metodunun uygulaması, generik nesne sınıfı gibidir. Bu metot, generik nesne sınıfı prototipinden kalıtımla fonksiyon nesne sınıfına aktarılmış, fakat fonksiyon nesne sınıfında içeriği yenilenmiş ve fonksiyon nesne sınıfına uygun hale getirilmiştir. Yani toString() metoduna Function nesne sınıfında bindirilmiştir.

Fonksiyon nesne sınıfının prototipinin içeriğini toString() metodu ile yazdırmak olanağı bulunamaz. Çünkü, bu fonksiyonun yapısı kullanıcıya kapalıdır. Bunun yerine, bir fonksiyon nesne sınıfı örneğinin içeriğini bu metodu kullanarak yazdırmaya çalışalım . Bunun için yazılan bir JavaScript programının, bir uygulama sayfasında verdiği sonuç aşağıda görülmektedir:

function çarp(x, y) {
return x * y;
}
		
...
sonuçYaz('çarp.toString() = ', çarp.toString(), 'b7.4.2-uyg-1-sonuç-1');
...
		

Program Sonucu :

7.4.3 - Function Nesne Sınıfı Prototip Nesnesinin apply() Metodu

(Dikkat ! İleri Düzey Metot ! Tüm JavaScript Çekirdek Özelliklerinin İncelenmesinden Sonra Okunması Sağlık Verilir!)

Bu metodun amacı, bir nesnesin metodunu başka bir nesne ile çağırabilir hale getirmektir. Yazılımı,

FonksiyonNesneSınıfıÖrneği.apply([thisNesnesi[, argümanDizisi]])
		

şeklindedir. Burada,

FonksiyonNesneSınıfıÖrneği : Bir fonksiyon bildiriminde, fonksiyonun ismi olabilir. Örnek,

function eklenti (x, y) { 
return x+y;
}

eklenti.apply( ...
		

vaya bir fonksiyon literali atanmış bir değişken olabilir. Örnek:

t = function() {...
			
t.apply( ...
		

( Burada t değişkenine, fonksiyonun bellekteki yerini işaret eden bir bellek işaretçisi (pointer) atanmıştır.)

veya bir fonksiyonun bellekteki yerini işaret eden bir bellek işaretçisi (pointer) atanmış olan bir değişken olabilir. Örnek:

t = function() {...

var p = t;

p.apply( ...
		

Bu metodun uygulanabilmesi için, bir fonksiyon nesne sınıfı örneğinin belirtilmesi gereklidir.

thisNesnesi : this saklı sözcüğünün geçerli olacağı nesne sınıfının adı (isteğe bağlı) eğer hiçbir sınıf adı belirtilmemişse, this saklı sözcüğünün belirteceği nesne Global JavaScript Nesnesi olur. (Bu argüman, bir nesne literali de olabilir.)

argüman dizisi : Elemanları fonksiyonun çağrılması için gerekli argümanlar olan dizi (fonksiyonun formal parametre dizisi) (isteğe bağlı) ( istenirse fonksiyonun öntanımlı argüman dizisi arguments olarak da belirtilebilir).

Basit bir uygulama için örnek JavaScript programı ve ilişikli olduğu, uygulama sayfasında verdiği sonuç, aşağıda görülmektedir:

function renkBilgisi(önEk, sonEk) {
alert(önEk + this.renk + sonEk);
}

varnesne = {}; 

nesne.renk = 'Kırmızı';
		
renkBilgisi.apply(nesne, new Array('Gerçekten', '\n Çok Güzel Bir Renktir!'));
		

Bu metodun amacı, this saklı sözcüğü kullanılarak, bir fonksiyonun birden çok nesne için metot olarak hareket etmesini sağlamaktır. Bu yöntemi kullanan bir JavaScript fonksiyonu ve ilişkilendirilmiş olduğu bir uygulama sayfasında verdiği sonuç, aşağıda görülmektedir:

/* <![CDATA[  */
			
/* Bu Program bdelib.js Kitaplık Programını Kullanmaktadır */

var x = 'Global x';

var k = {x : 16.44};

function mesajYaz() {
alert('x = ' + this.x); 
}

function mesaj2() {

mesajYaz();
}

function mesaj3() {

mesajYaz.apply(k);
}


function başlat2(){

var a = document.getElementById('b7.4.3-uyg-2-sonuç-1'),
b= document.getElementById('b7.4.3-uyg-2-sonuç-2');

a.onclick = mesaj2;
b.onclick = mesaj3;
}


sayfaYüklendiktenSonraÇalıştır(başlat2);


/* ]]> */
		

Bu programda,

mesajYaz.apply(k);
		

yerine,

mesajYaz.apply(k, arguments);
		

yazılabilirdi. Buradaki arguments dizi olarak hareket eden öntanımlı nesne, fonksiyon kodlarının çalıştırma kapsamına girdiğinde otomatik olaraki oluşturulur. Bu diziye benzer bir öntanımlı nesnedir ve apply metodunun ikinci parametresi olan fonksiyon argümanlarının yerini tutar.Biraz ileride, arguments nesnesini daha yakından inceleyeceğiz. JavaScript fonksiyonları argümanlarının çok esnek bir şekilde kullanılmasına olanak sağlar. Tanım şırasında belirtilen formal parametre listesine uyulması zorunluğu yoktur. Fonksiyon belirtilen parametrelerle verbileceği sonuçları verir. Doğal olarak, verilen parametrelerle bir sonuç alınamıyorsa hata oluşur. Bazı durumlarda, fonksion sıfır parametre ile tanımlanabilir, fakat bir parametre listesi ile çağrılabilir. Bu durumda belirtilen parametreler, arguments dizisinin elemanları gibi düşünülür ve işleme girerler. Bu konuda arguments dizisi incelendiğinde örnekler verilecektir.

Burada incelemekte olduğumuz, apply() meetodu, JavaScript programlanması için az kulanılan metotlardandır. Bu metot ile gelişkin uygulamalar yapılması Olasıdır. Bu uygulamalardan biri John Resig tarafından düzenlenmiştir. Gelişkin uygulamalar için, her zaman faydalı kısımlar, sakıncalı olabilecek yönler ile birlikte düşünülmelidir.

İleri tekniklerden birisi de, Mike West tarafından geliştirilen bind() fonksiyonudur. Aşağıda, bir JavaScript programı ile bu fonksiyon uygulanmaktadır. Programın iliştirildiği web sayfasında verdiği sonuçlar aşağıda görüntülenmektedir:

/* <![CDATA[  */
			
/* Bu Program bdelib.js Kitaplık Programını Kullanmaktadır */

Function.prototype.bağla = function(nesne) {
var metot = this, 
geçici = function () {
return metot.apply(nesne, arguments);
}
return geçici;
}


function çarpım(değer) {
return this * değer;
}


function mesaj4() {
var ilkNesne = {sayı : 12},
ilkÇarpım = çarpım.bağla(ilkNesne);

alert(ilkNesne.sayı + ' + ' + 'ilkÇarpım'(5));}


function mesaj5() {

var ikinciNesne = {sayı : 24},
ikinciÇarpım = çarpım.bağla(ikinciNesne);

alert(ikinciNesne.sayı + ' + ' + 'ikinciÇarpım'(5));
}


function başlat3(){

var a = document.getElementById('b7.4.3-uyg-3-sonuç-1'),
b= document.getElementById('b7.4.3-uyg-3-sonuç-2');

a.onclick = mesaj4;
b.onclick = mesaj5;
}


sayfaYüklendiktenSonraÇalıştır(başlat3);


/* ]]> */
		

Bu uygulama, tam bir polimorfi örneğidir, çünkü tek metodu iki nesne paylaşabilmektedir. Bunun gerçekleştirilebilmiş olması ise fazlası ile karmaşık mekanizmalar sayesinde olabilmektedir. İlk olarak, Function nesne sınıfı prototipini genişleten bağla () fonksiyonu, Function nesne sınıfı prototipine yeni bir metot olarak eklenmektedir. Bu şekilde, bağla () metodu, programdaki tüm fonksiyonlar için erişilebilir olmaktadır. Genel olarak, öntanımlı nesne sınıflarının prototiplerine katkı yapmak çok olumlu bir yöntem olarak kabul edilmez. Bu durum, programda kullanılan tüm fonksiyonlarının tazşınabilirliğini olumsuz olarak etkiler. Fakat, bilinçli olarak kullanılırsa, bu durumun tolere edilebileceği söylenebilir.

bağla() Metodunun yazılımında, this değeri, metodun uygulandığı fonksiyon, yani çarpım() fonksiyonu olarak belirlenir. bind() Metodunun argümanı olan nesne çarpım metodunun bağlanacağı nesneyi işaret eder. Bundan sonra bir kapalı devre (closure) yaratılmaktadır. Bu kapalı devre, üst çalışma alaınından, this bir markalanmış metodun adını almakta ve metodu nesneye bağlayarak sonucu döndürmektedir. Kapalı devrenin döndürdüğü sonuç, foksiyonun sonucu olarak geri döndürülmektedir.

Bu şekilde, aynı çarpım() fonksionu, bağla() metodu yardımı ile, bir ilkNesneye bir ikinciNesneye bağlanarak polimorfi gerçekleştirilmiştir.

bağla() Metodu, olay yöneticilerine doğru nesneyi bağlamak için kullanılabilir. Aşağıdaki JavaScript programı karışık this saklı sözcüklerinin hedef şaşırtabileceği durumlarda, fonksiyonun doğru nesneyi işaret etmesi için yararlı olmaktadır. Bu programın bağlantılı olduğu bir uygulama sayfasında verdiği sonuç, aşağıda görülmektedir:


/* <![CDATA[  */
			
/* Bu Program bdelib.js Kitaplık Programını Kullanmaktadır */

Function.prototype.bağla = function(nesne) {
var metot = this, 
geçici = function () {
return metot.apply(nesne, arguments);
}
return geçici;
}


function BüyükSoru(yanıt);
this.gerçekYanıt = yanıt;
this.soruSor = function() {
alert(this.gerçekYanıt);
}
}


function olayYöneticisiEkle();

var büyükDüşünce = new BüyükSoru('Gerçek Yanıt, Hiçbir Yanıt Olmadığıdır !'),
düğme = document.getElementById('b7.4.3.-uyg-4-düğme-1');

düğme.onclick = büyükDüşünce.soruSor.bağla(büyükDüşünce);
}


sayfaYüklendiktenSonraÇalıştır(olayYöneticisiEkle);
		
/* ]]> */
		

Bu uygulamada da görüldüğü gibi, this saklı sözcüğü, program akışı içinde bukalemun gibi anlam değiştirmektedir. BüyükSoru() fonksiyonu bir yapılandırıcı fonksiyondur ve new BüyükSoru(değer); şeklinde yeni bir BüyükSoru sınıf örneğini yapılandırırken, this saklı sözcükleri yeni yapılandırılacak nesne özelliğinin özellikleri olarak değerlendirilir. Fakat sorSor() metodunun içindeki alert(this. gerçekYanıt); satırındaki, this saklı sözcüğü, BüyükSoru sınıfından yeni yapılandırılan nesne örneğini değil, fonksiyonu çağıran DOM düğme elementini belirtmekte ve bunun da bir gerçekYanıt özelliği olmadığından, normalde program hata vermektedir. Burada bağla() fonksiyonu metodu yeni yapılandırılan büyükDüşünce adlı, BüyükSoru nesne sınıfı örneğine bağlayarak, doğru yeri işaret etmesini sağlamakta ve programı çalıştırmaktadır. Unutulmaması gereken, bu olanağın, Function nesne sınıfı prototipinin arttırılması pahasına elde edilmiş olmasıdır.

Yukardıdaki uygulama ile aynı sonucu, biraz düzenleme ile Function nesne sınıfı prototipinin arttırılmasınıa neden olan bağla() fonksiyonunun uygulanmasına gerek kalmadan da alabiliriz. Bunu sağlayacak JavaScript programı ve bağlı olduğu, uygulama sayfasında verdiği sonuç, aşağıda görülmektedir:


/* <![CDATA[  */
			
/* Bu Program bdelib.js Kitaplık Programını Kullanmaktadır */

function BüyükSoru(yanıt);
this.gerçekYanıt = yanıt;
this.soruSor = function() {
alert(this.gerçekYanıt);
}
}

function açıkla();

var büyükDüşünce = new BüyükSoru('Gerçek Yanıt, Hiçbir Yanıt Olmadığıdır !'),

büyükDüşünce.soruSor();
}


function olayYöneticisineBağla();

düğme = document.getElementById('b7.4.3.-uyg-5-düğme-1');

düğme.onclick =açıkla;
}


sayfaYüklendiktenSonraÇalıştır(olayYöneticisineBağla);
		
/* ]]> */
		

Bu uygulama ile bir önceki uygulama arasındaki fark, BüyükSoru() nesne sınıfının bir örneği olan büyükDüşünce nesnesinin soruSor() metdounun sahibinin değiştirilmiş olmasıdır. İlk uygulama da metot,


düğme.onclick = büyükDüşünce.soruSor.bağla(büyükDüşünce);
		

şeklinde çağrıldığından, metodunun sahibi, metodu çağıran düğme1 nesnesidir ve bunun da, bir gerçekYanıt özelliği olmadığından sonuç undefined olarak elde edilmektedir. Bu nedenle, bağla() fonksiyonu kullanılarak, çağrının yeni nesne örneğine bağlanması sağlanmış, fakat bunun bir yan etkisi olarak Function nesne sınıfının prototype özelliğine yeni bir özellik eklenmesi gerekmiştir. Bir tür gizli global olan bu işlemden kurtulmak için, bağla() fonksionunun kullanımını gerektirmeyen bir tasarım olan, ikinci uygulamada ise, çağıyı yapan doğrudan yani nesne örneğidir ve bunun da gerçekYanıt özelliği olduğundan, sonuç doğru çıkmakta, bağla() fonksionunun kullanımına gereksinme kalmamaktadır. Sonuçta, her iki fonksiyon da aynı işi yapmakta ama sonuncusunun hem kodları daha açık hem de taşınabilirliği daha yüksek olmaktadır.

Eski bir atasözünün dediği gibi, "When There Is A Will, There Is Allways A Way" yani, istek olduğunda daima bir yol vardır. Kendi başına, apply() metodunun uygulanması için hiçbir engel yoktur, fakat, öntanımlı bir nesne sınıfı prototipinin değiştirilmesi, çok büyük bir gereksinme yoksa, kesinlikle uzuak durulması gereken bir eylemdir. Bu durumda, tutulması gereken yol, eğer yapılabiliyorsa, bizim de bu uygulamalarda yaptığımız gibi, tasarım değiştirmek ve öntanımlı bir nesne sınıfı prototipinin değiştirilmesini gerektirmeyecek bir tasarım geliştirmektir.

JavaScript programlama dili, gerçekten çok esnek, olanakları çok geniş ve teknik temelleri çok sağlam bir programlama dilidir. Kendi dil yapısı sıkı bir şekilde nesne yönelimli olmasına karşın, uygulamalarında hem prosedüral hem de nesne yönelimli yöntemleri aynı etkinlikle destekleyebilien, belki de ADA program dilinden sonra ikinci sırayı alabilecek bir programlama dildir. Buna rağmen her yapılabilenin mutlaka yapılması gerekir diye bir kural yoktur. En profesyonel uygulama en karışık olan değil, verilen işi yapan, en kolay okunur, en açık yazılımı olan ve en kolay bakımı yapılabilen bir programdır.

7.4.4 - Function Nesne Sınıfı Prototip Nesnesinin call() Metodu

(Dikkat ! İleri Düzey Metot ! Tüm JavaScript Çekirdek Özelliklerinin İncelenmesinden Sonra Okunması Sağlık Verilir!)

Bu metodun amacı, aynen apply() metodu gibi, bir nesnenin metodunu başka bir nesne ile çağırabilir hale getirmektir. Yazılımı,

FonksiyonNesneSınıfıÖrneği.call([thisNesnesi[, argüman1, argüman2, ...]])
		

şeklindedir. Burada ikinci parametre gurubu, bir dizi değildir, argümanlar sıra ile belirtilir. Ayrıca, ikinci parametre olarak öntanımlı arguments saklı sözcüğü de kullanılmaz. Bunun dışında, call() metodu ile apply() metotlarının kullanım yerleri aynıdır.

Örnek olarak uygulama sayfası ile ilişikli, JavaScript fonksiyonunun,

function mesaj3() {

mesajYaz.apply(k);
}
		

kısmı istenirse,

...

mesajYaz.call(k);
...
		

olarak değiştirilebilir. Aşağıda JavaScript kodları verilmiş olan uygulamada, bu değişiklik yapılmıştır.

/* <![CDATA[  */
			
/* Bu Program bdelib.js Kitaplık Programını Kullanmaktadır */

var x = 'Global x';

var k = {x : 16.44};

function mesajYaz() {
alert('x = ' + this.x); 
}


function mesaj2() {

mesajYaz();
}


function mesaj6() {

mesajYaz.call(k);
}


function başlat4(){

var a = document.getElementById('b7.4.4-uyg-1-sonuç-1'),
b= document.getElementById('b7.4.4-uyg-1-sonuç-2');

a.onclick = mesaj2;
b.onclick = mesaj6;
}


sayfaYüklendiktenSonraÇalıştır(başlat4);


/* ]]> */
		

Yukarıdaki uygulamanın, sonuçlarından da görüldüğü gibi, apply() ve call() metotlarının kullanım amaçları birbirlerinin aynıdır. Sadece apply() metodu daha kurumsal, call() metodu daha bireyseldir. Koşullar uygun oldukça, programcılar ikisinden birini rahatlıkla seçebilirler.

Valid XHTML 1.1

-->