JavaScript Temelleri

Bölüm 15

Kullanıcı Tanımlı Nesneler

Bölüm 15 Sayfa 7

15.6 - Örnek Proje

Bu bölümde incelenmiş olan yöntemlerin uygulanması, örnek bir proje çerçevesinde gerçekleştirilmiştir. Bu projede bir sosyal klübün üye kayıtları incelenecektir.

Bu projede, genel üyelere ait veri yapısı, ayrı bir veri dosyasında saklanarak yanlışıkla değiştirilmesi olasılığının azaltılması amaçlanmıştır. Her bir genel üye kaydı, GenelÜye sınıfına ait yeni bir nesne örneği anlamına gelmektedir. Bu yapılandırıcı fonksiyon, aşağıda görülmektedir:

function GenelÜye(nr,ad,soyad) {
this.nr=nr || 0;
this.ad=ad || 'Veri Atanmadı';
this.soyad=soyad || '';
}
		

Veri yapısılarına ait nesne örneklerinin oluşturulması amaçlı ilk web formu, bölüm 15.2.2 de bir uygulama da tanıtılmıştı. Bu uygulamada, veri kaydı yapıldıktan sonra, kullanıcının giriş düğmesini tekrar tıklaması ile yeniden kaydın tekrarlanması sorunu bulunmaktaydı ve bu nedenle, gerçek ortamlarda uygulanması uygun değildi.

Bu projede, ilk olarak, GenelÜye sınıfına ait kayıtların girilebileceği gerçek bir web formu geliştirilecektir. Bu formda, kullanıcıların kayıt düğmesine sadece ilk bastıklarında programın çalışacağı, sonraki tıklamaların etkisiz olacağı bir yöntem geliştirilmiştir. Bu programın JavaScript kodları aşağıda görülmektedir.

...
// çalıştır() fonksiyonu bdelib.js program kitaplığındadır.
// oku() fonksiyonu bdelib.js program kitaplığındadır.
// nesneTablosu() fonksiyonu bdelib.js program kitaplığındadır.

// GenelÜye Yapılandırıcı Fonksiyonu b15.7-uyg-genel-ilk.js kitaplığındadır.


function verigir() {
var gizli = 1, klüpÜyesi = = [], geriDönüş = false;
this.sınırlama = function() {
if (gizli > 0) {
gizli = gizli - 1;
klüpÜyesi[1] = yeniGenelÜyeYaz();
if (klüpÜyesi) {
nesneTablosu('kayıt', 'Klüp Üyesi Kaydı', klüpÜyesi[1);
yaz('rec', 'KayıtTamamlandı !');
geriDönüş= false;// çapa elementinin href niteliğinin etkisizleştirilmesi için...
}
else {
geriDönüş= true;// çapa elementinin href niteliğinin etkinleştirilmesi için...
}

else {
geriDönüş= false;// çapa elementinin href niteliğinin etkisizleştirilmesi için...
}
return geriDönüş;
}
}

function yeniGenelÜyeYaz() {

var klüpÜyesi = null, a = null, b = null, c = null;

a = oku('nr');

b = oku('ad');

c = oku('soyad');

if (tenisÜyesiYaz.ilkÇağrı) {

if(! isFinite(parseInt(a)) ||  isFinite(parseInt(b , 10)) || isFinite(parseInt(c , 10)) || b === '' || c === '') {

alert('Geçersiz Veri !\n' + 'Lütfen O.K. Tuşuna Bastıktan Sonra\n' + 'Düzeltilmiş Verileri Giriniz');
klüpÜyesi = false;
}

else {
// Kaydı Yap !

klüpÜyesi = yeniGenelÜye(a, b,c) ;
}

return klüpÜyesi;
}


function başlat() {
var yeniKayıt = new verigir();

çalıştır('kayıtYap' , verigir.sınırlama);

}

...
		

Kayıt başarılı olursa, kaydedilen verilerin listesi, bdelib kitaplığında bulunan, nesneTablosu() adında çok esnek ve genel bir tablolama fonksiyonu ile yapılmaktadır. Bu programın yazılımı aşağıda verilmiştir.



function nesneTablosu(oluşacakTablo , tabloBaşlığı , nesne);
var tablo = document.getElementById('oluşacakTablo'),
capt = document.createElement('caption'),
tabloGövdesi = document.createElement('tbody'),
tabloGövdesi = document.createElement('tbody'),
yeniSatır = document.createElement('tr'),
yeniSütun = {};

tablo.setAttribute('border', '3px' );
tablo.setAttribute('align', 'center' );
tablo.setAttribute('style', 'font : 0.92em Verdana;' );


capt.setAttribute('class', 'cursive-red' );
capt.appendChild(document.createTextNode(tabloBaşlığı);
tablo.apppendChild(capt);

tablo.apppendChild(tabloGövdesi);

yeniSatır = document.createElement('tr');
yeniSatır.setAttribute ('class', 'cursive-maroon');
yeniSatır.setAttribute('style', 'background-color : orange;' );
tabloGövdesi.apppendChild(yeniSatır);

yeniBaşlık = document.createElement('th');
yeniBaşlık.appendChild(document.createTextNode('Veri Alanı');
yeniSatır.apppendChild(yeniBaşlık);

yeniBaşlık = document.createElement('th');
yeniBaşlık.appendChild(document.createTextNode('Veri Alanı Değeri');
yeniSatır.apppendChild(yeniBaşlık);
for(var alan in nesne {
if (typeof nesne[alan] === 'function'){
continue;
}

else {
yeniSatır = document.createElement('tr');
tabloGövdesi.apppendChild(yeniSatır);

yeniSütun = document.createElement('td');
yeniSütun.setAttribute ('class', 'cursive-red');
yeniSütun.appendChild(document.createTextNode('Tenis Üyesinin Üye Numarası');
yeniSatır.apppendChild(yeniSütun);

yeniSütun = document.createElement('td');
yeniSütun.setAttribute ('class', 'cursive-blue');
yeniSütun.appendChild(document.createTextNode(nesne['nr']);
yeniSatır.apppendChild(yeniSütun);
}
}
}

		

Kullanılan nesneTablosu() fonksiyonu, çok genel bir fonksiyon olduğundan, her nesne örneği için uygulanabilir. Buna karşılık, özellik isimleri genel olarak özellik olarak görülmektedir. Bu fonksiyon, kolaylıkla özelleştirilebilir.

Bu veri giriş formu, uygulama sayfasında denenebilir.

Yukarıdaki programı, daha önce, bölüm 15.2.2 de verilmiş olan programına göre daha gelişkin bir programdır. Bu program, sadece ilk tıklamanın kayıt yapılmasını sağlayacak ve global değişkenlerin kullanılmadığı bir sınırlama yöntemini içermektedir. Böyle bir sınırlama web formları için, ancak gizli veya açık global değişkenler kullanılması ile gerçekleştirilebilir.

Yukarıdaki program kullanıcının formu sunmak için basacağı sunuş düğmesine sadece bir kez basabiilmesini sağlayan bir sınırlama yöntemi içermektedir. Bu bölüm 15.2.2 de verilmiş olan programa göre önemli bir ilerlemedir. Bu ilerlemenin değerlendirilebilmesi için önce genel olarak tıklama sınırlamasının nasıl gerçekleştirilebileceği incelenecektir. Bu gibi hareketli programların akım şemaları ile açıklanması oldukça zordur. Bunun için, olayları izlemeye çalışacağız. İlk olarak, programını ele alalım burada hiç tıklama kontrolü yoktur. Kullanıcı giriş düğmesini tıklayınca, program, her giriş kutusundakii değerin uygunluğunu kontrol etmektedir. Eğer değer uygun değilse kullanıcı uyarılmakta fakat yine de kabul edilmektedir. Sonuçta kayıt gerçekleşmekte fakat sayfa açık kalmakta ve kayıt düğmesinin yeniden tıklanmasını engelleyecek bir kontrol bulunmamaktadır ve her tıklayışta istendiği kadar aynı değerleri içeren tekrarlı kayıtlar gerçekleştirilebilmektedir. Bu da en istenmeyen şeydir. Bir kayıt dosyasında, birbirinin aynı iki kayıt asla bulunmamalıdır. Bu nedenle böyle formlar gerçek uygulamalarda kullanılmamalıdır.

Bu durumun düzeltilmesi, pensip olarak sabit bir çapa görevini yapacak bir global değişken kullanılması ile çözümlenebilir. Global değişken başlangıçta true değerindedir. Kullanıcı kayıt düğmesine tıkladığında, program kontrolü global değişkeni kontrol eder, değer true ise bu değeri false yapasr ve giriş değerlerini tek defada kontrol eder. Bu değerler doğruysa kayıt gerçekleşir. Değilse, bir uyarı mesajı görüntülenir ve kullanıcı bu mesajı okuduğunu belirten düğmeye tıklarsa, sayfa yeni bir kayıt akımı açar. Kayıt gerçekleştikten sonra, kullanıcı yeniden kayıt düğmesine tıklasa bile kayıt gerçekleşmez çünkü global çapanın değeri false, yani bayrak inik durumdadır.

Bu sistemde tüm giriş verilerininin uygunluğunun ancak tüm değerlerin birden tek defada kontrolü ile yapılabildiğini de ekleyelim. Bu yöntem ister istemez tüm istemci tarafı programlarında böyledir. Çünkü bir web sayfasının durdurulup yeniden başlatılması olanağı yoktur.

Global bir çapanın kullanılması, tıklama kontrolü problemini çözmekte fakat, global değişken kullanımının istenmeyen yan etkileri nedeni ile, kullanımına pek sıcak bakılmamaktadır. Tıklama kontrolünün başka bir yöntemle yapılması için burada sınıf özel üyelerinden yararlanan yeni bir yöntem geliştirilmiştir. Özel üyelerin kullanımına ait uygulama, ilk olarak JavaScript uzmanı, Douglas Crokford tarafından önerilmiştir. Bu fonksiyonun orijinalini daha önce incelenmiştik. Burada, bu fonksiyon, form tıklamasının sınırlandırılması amacı ile yeniden düzenlenmiştir. Metot, bir verigir() yapılandırıcı fonksiyonun ileride anlamı açıklanacak olan ayrıcaklı sınırlama () metodunun, fonksiyonun özel üyesi olan gizli isimli bir yerel değişkenin değerini okuması, her okuyuşta bu özel üyenin değerini değiştirmesi ve her çağrılırılışta bir önceki değerini okumaya çalışmasına dayanmaktadır. Tek defalık bir okuma va değiştirmeden sonra artık bu metot geriye daima false değerini döndürmektedir. Bu değeri okuyan tıklama kontrolü de sadece bir kez çalışmakta ve ikinci kez tıklandığında işlevsiz olmaktadır. Sayfa, uygun olmayan veriler girildiğinde, kaydı yapmayarak kendini yenilemekte ve verilerin yeniden doğru olarak girilmesini beklemektedir. Sadece uygunluk kontrollerini geçebilen veriler, kayda girebilmektedir. İleride bu veri giriş formları, verilerin disk dosyalarına kaydedilmesi için kullanılacaktır.

Özellikle, yukarıdaki formda, bir olay yöneticisine bir fonksiyon yerine bir metodun atanması orijinal bir düşüncedir ve global değişken kullanma gereksinmesini ortadan kaldırmaktadır.

Yukarıdaki formda, tıklama sınırlamasının gerçekleştirilmesi, formun istemci bilgisayarına indirildiği durumda, istemcinin JavaScript yorumlayıcısı devreden çıkarılmış ise, çalışmayacaktır. Bu tür formların sunucu tarafında çalıştırılması daha garantili olacaktır.

Klüp bir süre üyelerine hizmet vermiş ve yeni spor tesisleri inşaatları yaptırmıştır. Bu tesislerden ilk hizmete gireni tenis spor dalıdır. Klübün Tenis seksiyonu açılınca, Tenis seksiyonu için yeni üyelerin kayıtlarının yapılması gerekmiştir. Kaydını yaptıracak yeni üyeler, tenis tesislerinden de yararlanmak isterlerse, bunu ilk kayıtları sırasında belirtmeleri istenmiştir. Bu üyelerin kayıtları için, TenisÜyesi () isimli yeni bir yapılandırıcı fonksiyon düzenlenmiş ve bu fonksiyon GenelÜye sınıfı yapılandırıcı fonksiyonunun bulunduğu sınıf yapıları dosyasına eklenmiştir. Tenis üyeleri sınıfı, genelÜye sınıfının bir alt sınıfı olarak oluşturulmuştur. Alt sınıf/ üst sınıf ilişkisi, TenisÜyesi() yapılandırıcı fonksiyonunun üst sınıf olan GenelÜye() yapılandırıcı fonksiyonunu çağırması ile gerçekleştirilmiş ve buna ek olarak üst sınıf prototipinin alt sınıfa transferi ile de sınıflararası hiyerarşik prototip zinciri kurulmuştur. Veri yapıları ve yapılandırıcı fonksiyonlar, kendi sayfalarında ve aşağıda görülebilir.

...

function GenelÜye(nr,ad,soyad) {
this.nr=nr || 0;
this.ad=ad || 'Veri Atanmadı';
this.soyad=soyad || '';
}

function TenisÜyesi(nr,ad,soyad,dolapNo) {
this.kalıtım=GenelÜye;
this.kalıtım(nr,ad,soyad);
this.dolapNr=dolapNo || 0 ;
}


TenisÜyesi.prototype = new GenelÜye();
...
		

Projenin başlangıcından itibaren belirli bir süre geçmiş olduğundan, veri giriş formunda, hem program yapısı açısından hem de görüntü açısından bazı özelleştirmeler gerçekleştirilmiştir. bu yeni sürümde, veri giriş formunde değişken isimleri daha anlamlı olanları ile yer değiştirmiş ve veri giriş sonucunun görüntülenmesi için yararlanılan tablolama fonlsiyonu da daha özelleştilimiş bir hale getirilmiştir. Bu değişikliler, kendi sayfalarında ve aşağıda görülebilir:

...

function tabloYap(oluşacakTablo , tabloBaşlığı , nesne);
var tablo = null;
var capt = null;
var tabloGövdesi = null;
var yeniSatır = null;
var yenibaşlık = null;
var yeniSütun = null;

tablo.document.getElementById('oluşacakTablo');
tablo. style. font = '16px verdana';
tablo. border = '3px';
tablo. align = 'center';

capt.document.createElement('caption');
capt.className = 'cursive-red';
capt.appendChild(document.createTextNode(tabloBaşlığı);
tablo.apppendChild(capt);

tabloGövdesi = document.createElement('tbody');
tablo.apppendChild(tabloGövdesi);


yeniSatır = document.createElement('tr');
yeniSatır.className = 'cursive-maroon';
yeniSatır. style. backgroundColor = 'orange';
tabloGövdesi.apppendChild(yeniSatır);


yeniBaşlık = document.createElement('th');
yeniBaşlık.appendChild(document.createTextNode('Veri Alanı');
yeniSatır.apppendChild(yeniBaşlık);

yeniBaşlık = document.createElement('th');
yeniBaşlık.appendChild(document.createTextNode('Veri Alanı Değeri');
yeniSatır.apppendChild(yeniBaşlık);

yeniSatır = document.createElement('tr');
tabloGövdesi.apppendChild(yeniSatır);

yeniSütun = document.createElement('td');
yeniSütun.className = 'cursive-red';
yeniSütun.appendChild(document.createTextNode('Tenis Üyesinin Üye Numarası');
yeniSatır.apppendChild(yeniSütun);

yeniSütun = document.createElement('td');
yeniSütun.className = 'cursive-blue';
yeniSütun.appendChild(document.createTextNode(nesne['nr']);
yeniSatır.apppendChild(yeniSütun);

yeniSatır = document.createElement('tr');
tabloGövdesi.apppendChild(yeniSatır);

yeniSütun = document.createElement('td');
yeniSütun.className = 'cursive-red';
yeniSütun.appendChild(document.createTextNode('Tenis Üyesinin Adı');
yeniSatır.apppendChild(yeniSütun);

yeniSütun = document.createElement('td');
yeniSütun.className = 'cursive-blue';
yeniSütun.appendChild(document.createTextNode(nesne['ad']);
yeniSatır.apppendChild(yeniSütun);

yeniSatır = document.createElement('tr');
tabloGövdesi.apppendChild(yeniSatır);

yeniSütun = document.createElement('td');
yeniSütun.className = 'cursive-red';
yeniSütun.appendChild(document.createTextNode('Tenis Üyesinin Soyadı');
yeniSatır.apppendChild(yeniSütun);

yeniSütun = document.createElement('td');
yeniSütun.className = 'cursive-blue';
yeniSütun.appendChild(document.createTextNode(nesne['soyad']);
yeniSatır.apppendChild(yeniSütun);

yeniSatır = document.createElement('tr');
tabloGövdesi.apppendChild(yeniSatır);

yeniSütun = document.createElement('td');
yeniSütun.className = 'cursive-red';
yeniSütun.appendChild(document.createTextNode('Tenis Üyesinin Dolap Numarası');
yeniSatır.apppendChild(yeniSütun);

yeniSütun = document.createElement('td');
yeniSütun.className = 'cursive-blue';
yeniSütun.appendChild(document.createTextNode(nesne['dolapNr']);
yeniSatır.apppendChild(yeniSütun);
}


// çalıştır() fonksiyonu bdelib.js program kitaplığındadır.
// oku() fonksiyonu bdelib.js program kitaplığındadır.
// nesneTablosu() fonksiyonu bdelib.js program kitaplığındadır.

// GenelÜye() ve TenisÜyesi() Yapılandırıcı Fonksiyonları, 
// b15.7-uyg-genel-ikinci.js kitaplığındadır.


function verigir() {
var gizli = 1, tenisÜyesi = = [], geriDönüş = false;
this.sınırlama = function() {
if (gizli > 0) {
gizli = gizli - 1;
tenisÜyesi[1] = yeniTenisÜyesiYaz();
if (tenisÜyesi[1]) {
tabloYap('kayıt', 'Tenis Üyesi Kaydı', tenisÜyesi[1);
yaz('rec', 'KayıtTamamlandı !');
geriDönüş= false;// çapa elementinin href niteliğinin etkisizleştirilmesi için...
}
else {
geriDönüş= true;// çapa elementinin href niteliğinin etkinleştirilmesi için...
}

else {
geriDönüş= false;// çapa elementinin href niteliğinin etkisizleştirilmesi için...
}
return geriDönüş;
}
}

function yeniTenisÜyesiYaz() {

var tenisÜyesi = null, tenisÜyesiÜyeNumarası = null, tenisÜyesiAdı = null, 
tenisÜyesiSoyadı = null,  tenisÜyesiDolapNumarası= null;

tenisÜyesiÜyeNumarası= oku('nr');
tenisÜyesiAdı= oku('ad'); 
tenisÜyesiSoyadı= oku('soyad');
tenisÜyesiDolapNumarası= oku('dolap');

if(! isFinite(parseInt(tenisÜyesiÜyeNumarası)) ||  isFinite(parseInt(tenisÜyesiAdı, 10)) || 
isFinite(parseInt(tenisÜyesiSoyadı, 10)) || tenisÜyesiAdı === '' || tenisÜyesiSoyadı === '' || 
! isFinite(parseInt(tenisÜyesiDolapNumarası))) {

alert('Geçersiz Veri !\n' + 'Lütfen O.K. Tuşuna Bastıktan Sonra\n' + 'Düzeltilmiş Verileri Giriniz');
tenisÜyesi = false;
}

else {
// Kaydı Yap !

tenisÜyesi = yeniTenisÜyesi(tenisÜyesiÜyeNumarası, tenisÜyesiAdı,tenisÜyesiSoyadı, tenisÜyesiDolapNumarası);
}

return tenisÜyesi;
}


function başlat() {
var yeniKayıt = new verigir();

çalıştır('kayıtYap' , verigir.sınırlama);

}
...
		

Bu yeni veri giriş formunu lütfen deneyiniz.

Çalışmalar devam ederken klüp yöneticileri, üyelerin telefon numaralarının kaydedilmesinin yararlı olacağını düşünerek bu yönde bir istek belirtmişlerdir. Projenin çok esnek veri yapısı ve veri giriş sistemi olduğundan bu isteğin gerçekleşmesi çok basit olacaktır. GenelÜye ve TenisÜyesi veri yapıları birbirlerine prototip zinciri ile bağlıdır ve GenelÜye sınıfı, üst sınıf konumundadır. Prototip zinciri kurulmuş olduğunda, üst sınıfın prototipine yapılacak bir alan eklentisi, anında alt sınıfın prototipine de yansıyacaktır.

Telefon numarası alanını sisteme yerleştirmek çok kolay olacaktır. İlk olarak, veri yapıları dosyasında,

GenelÜye.prototype.telNo = 'Veri Girilmedi !';
		

şeklinde yeni bir üst sınıf veri alanı tanıtımı yapılacaktır. Bu yeni üst sınıf veri alanı, alt sınıf olan TenisÜyesi sınıfının da veri yapısına aktarılmış olmaktadır. Bundan sonra, tabloYap()fonksiyonuna aşağıdaki satırlar eklenecektir :


yeniSütun = document.createElement('td');
yeniSütun.className = 'cursive-red';
yeniSütun.appendChild(document.createTextNode('Tenis Üyesinin Telefon Numarası');
yeniSatır.apppendChild(yeniSütun);

yeniSütun = document.createElement('td');
yeniSütun.className = 'cursive-blue';
yeniSütun.appendChild(document.createTextNode(nesne['telNo']);
yeniSatır.apppendChild(yeniSütun);
		

Bundan sonra Veri Giriş Formuna yeni bir telefon adlı veri alanı eklenecektir. Bundan sonra, program sayfasına aşağıdaki değişiklik yapılacaktır :

...
function yeniTenisÜyesiYaz() {

var tenisÜyesi = null, tenisÜyesiÜyeNumarası = null, tenisÜyesiAdı = null, 
tenisÜyesiSoyadı = null,   tenisÜyesiTelefonNumarası= null, tenisÜyesiDolapNumarası= null;

tenisÜyesiÜyeNumarası= oku('nr');
tenisÜyesiAdı= oku('ad'); 
tenisÜyesiSoyadı= oku('soyad');
tenisÜyesiTelefonNumarası= oku('telefon');
tenisÜyesiDolapNumarası= oku('dolap');

if(! isFinite(parseInt(tenisÜyesiÜyeNumarası, 10)) ||  isFinite(parseInt(tenisÜyesiAdı, 10)) || 
isFinite(parseInt(tenisÜyesiSoyadı, 10)) || ! isFinite(parseInt(tenisÜyesiTelefonNumarası , 10)) || 
tenisÜyesiAdı === '' ||tenisÜyesiSoyadı === '' || ! isFinite(parseInt(tenisÜyesiDolapNumarası))) {

alert('Geçersiz Veri !\n' + 'Lütfen O.K. Tuşuna Bastıktan Sonra\n' + 'Düzeltilmiş Verileri Giriniz');
tenisÜyesi = false;
}

else {
// Kaydı Yap !

tenisÜyesi = yeniTenisÜyesi(tenisÜyesiÜyeNumarası, tenisÜyesiAdı,tenisÜyesiSoyadı, 
tenisÜyesiDolapNumarası);

tenisÜyesi.telNo = tenisÜyesiTelefonNumarası;
}

return tenisÜyesi;
}
...
		

Bu formu lütfen deneyiniz.

Bu bölümde, kullanıcı tanımlı nesne sınıfları üzerinde kanamış olduğumuz bilgilerin bir projede uygulanması yapılmıştır. Örneklerde görülen tüm yöntemler, her türlü endüstriyel projede kullanım için uygundur. Bu yöntemlerin iyi özümsenmesi gerekmektedir.

Sonuç olarak, JavaScript program dilinde, hiyerarşik sistemler, çok kolay çözümlenebilmektedir. Bu konuda incelemiş olduğumuz örnekler, konu için belirli bir bilgi düzeyi sağlamaya yeterlidir. Bu örneklerin çok iyi özümsenmesi gereklidir.

JavaScript program dilinde, hiyerarşik sistemler üzerine daha birçok çalışma bulunmaktadır. Bu çalışmalar literatürden izlenebilir. Genel olarak, JavaScript program dilinde, hiyerarşik sistemlerin oluşturulması konvansiyonel olarak ve en alışılmış şekliyle yukarıdaki örneklerde görülen yöntemler uygulanarak çözümlenmektedir. Diğer çalışmalar henüz öneriler düzeyindedir ve genel bir uygulamaları yoktur. Ayrıca incelenen örneklerdeki yöntemlerden fazla bir avantaj sağlamamaktadır.

Valid XHTML 1.1