Javascript簡介
本指引基于Sergio Pereira的
Quick guide to somewhat advanced JavaScript tour of some OO features。
嘿,我不知道你能那樣做
如果你是一個Web開發者而且與我來自同一個地方,你也許在你的Web頁面中使用過相當多的Javascript,大部分用來作用戶界面(UI)的粘合。
直到現在,我才知道Javascript具有比我過去使用的更多的面向對象能力,只是我沒有如我所需的去使用它。當瀏覽器開始支持更標準的Javascript和DOM的功能集時,要為客戶端上的運行而編寫更復雜和多功能的代碼就變得可行了。它助長了AJAX現象的產生。
當我們都開始學習它適合編寫什么酷的AJAX應用時,我們開始關注到我們過去知道的Javascript不過只是冰山一角而已。我們現在知道Javascript不僅僅被用于象輸入校驗等簡單的用戶界面雜務和其他瑣碎任務。現在的客戶端代碼更高級和分層,更象一個真正的桌面應用或一個客戶端-服務器的富客戶端。我們在其中看到了過去僅在服務器端的代碼中見到的類庫、對象模型、繼承關系、模式和許多其他的東西。
突然在我們能講到的許多方面被放到了比以前更高的位置。要為新的Web編寫應用,我們必需提高我們的Javascript水平來跟上它而且要更加熟練。假如你嘗試運用許多現有的javascript庫,如:Prototype.js、Scriptaculous、moo.fx、Behaviour、YUI等,你最終會發現自己要閱讀JS代碼。或許因為你想要學習它們如何實現,或因為你的好奇,或通常因為那是知道如何使用它的唯一辦法,大多數這些庫象是不太注重文檔。無論個案會是什么,假如你之前不了解那樣的東西,你將要面對一些引起恐慌的外部技術。
本文的目的正好是解說許多我們還不熟悉的構造類型。
JSON (JavaScript Object Notation-Javascript對象表示法)
Javascript對象表示法(JSON),是圍繞AJAX主題突然出現的一個新的時髦詞。JSON,簡單的說就是一種在Javascript中定義對象的方法。讓我們馬上來看看一個實例并體會它如何簡單。
var myPet = { color: 'black', leg_count: 4, communicate: function(repeatCount){for(i=0;i<repeatCount;i++)alert('Woof!');}};
就讓我們加入些許格式讓它看起來象我們平常所認識的那樣:
var myPet =
{
color: 'black',
legCount: 4,
communicate: function(repeatCount){for(i=0;i<repeatCount;i++)alert('Woof!');
}};
在這我們創建了一個帶兩個屬性(color和legCount)和一個方法(communicate)的對象引用。不難發現,該對象的屬性和方法用一個逗號區隔的列表來定義。每個成員通過名稱來引入,接著是冒號和定義。對于屬性來說很容易,僅僅是屬性值。方法通過指派一個匿名函數來創建,我們下面會更好地說明。在該對象被創建并指派給變量myPet之后,我們可以如下使用它:
alert('my pet is ' + myPet.color);
alert('my pet has ' + myPet.legCount + ' legs');
//if you are a dog, bark three times:
myPet.communicate(3);
You'll see JSON used pretty much everywhere in JS these days, as arguments to functions, as return values, as server responses (in strings,) etc.
你想說什么?函數也是一個對象?
對于開發者來說這可能與眾不同,但在JS中一個函數也是一個對象。你可以象參數一樣把一個函數傳送給另一個函數,就象你可以傳送一個字符串一樣。這些被廣泛應用而且非常便利。
看看這個實例。我們將把函數傳送到另一個使用它們的函數去。
var myDog =
{
bark: function(){alert('Woof!');
}};
?
var myCat =
{
meow: function(){alert('I am a lazy cat. I will not meow for you.');
}};
?
function annoyThePet(petFunction){//let's see what the pet can do
petFunction();
}
?
//annoy the dog:
annoyThePet(myDog.bark);
//annoy the cat:
annoyThePet(myCat.meow);
注意,我們把沒有添加"()"的myDog.bark和myCat.meow傳送給它們。假如我們那樣做了,我們不是傳送函數,而是調用那些方法并傳送它們的返回值,這里的兩個個案中都未定義。
要是你想另我的懶貓起來嗥叫,你可以簡單地這樣做:
myCat.meow = myDog.bark;
myCat.meow(); //alerts 'Woof!'
數組、項目和對象成員
在JS中下列兩行做了同樣的事。
var a = new Array();
var b = [];
正如我確定你已經知道的那樣,你可以通過使用方括號在一個數組中訪問個別項目:
var a = ['first', 'second', 'third'];
var v1 = a[0];
var v2 = a[1];
var v3 = a[2];
但你還不限于數字索引。你可以通過使用它的名字以字符串來訪問一個JS對象的任何成員。下面的例子創建了一個空對象,并通過名字加入了一些成員。
var obj = {}; //new, empty object
obj['member_1'] = 'this is the member value';
obj['flag_2'] = false;
obj['some_function'] = function(){/* do something */};
上述代碼的作用與下面的相同:
var obj =
{
member_1:'this is the member value',
flag_2: false,
some_function: function(){/* do something */}};
在許多情形下,在JS中對象的概念和關聯的數組(雜湊)沒有區別。下面兩行代碼都做了同樣的事情。
obj.some_function();
obj['some_function']();
關于對象已經夠了,我現在能有一個類了嗎?
面向對象編程語言的巨大力量來源于類的使用。我不認為僅應用我之前使用其他語言的經驗就能推測出在JS中如何定義類。這由你自己來判斷。
//defining a new class called Petvar Pet = function(petName, age){this.name = petName;
this.age = age;
};
?
//let's create an object of the Pet classvar famousDog = new Pet('Santa\'s Little Helper', 15);
alert('This pet is called ' + famousDog.name);
讓我們看看如何加入一個方法到我們的Pet類。我們將使用所有類都具有的prototype屬性。prototype屬性是一個包含任何對象的類會具有的所有成員的對象。甚至默認的JS類,如String、Number和Date擁有一個的prototype對象,它能讓我們可以添加方法和屬性并使那個類的任何對象自動獲取這個新成員。
Pet.prototype.communicate = function(){alert('I do not know what I should say, but my name is ' + this.name);
};
那就是一個類似于prototype.js的庫會派上用場的時候。如果我們使用prototype.js,我們能使我們的代碼看起來更清晰(至少在我看來是這樣)。
var Pet = Class.create();
Pet.prototype =
{//our 'constructor'
initialize: function(petName, age){this.name = petName;
this.age = age;
},
?
communicate: function(){alert('I do not know what I should say, but my name is ' + this.name);
}};
函數作為參數,一個有趣的模式
假如你從來沒有接觸過支持閉合的語言,你也許會發現下面的慣用法太令人震驚了。
var myArray = ['first', 'second', 'third'];
myArray.each(function(item, index){alert('The item in the position #' + index + ' is:' + item);
});
哇!在你斷定我已經離題太遠并且想轉去比這篇更好點的文章之前,讓我們在這解釋一下要繼續干些什么。
首先,在上述實例中我們使用了prototype.js庫,它添加了each函數到Array類。該each函數接到一個函數對象的參數。這個函數對于數組中的每個項目都將被依次調用一次,對于當前項目調用時傳送兩個參數,item和index。讓我們稱這個函數為iterator函數。我們也能象這樣編寫代碼:
function myIterator(item, index){alert('The item in the position #' + index + ' is:' + item);
}
?
var myArray = ['first', 'second', 'third'];
myArray.each( myIterator );
我們不會象那些學校里的那些天真孩子那樣做的,對吧?雖然更嚴格地說,后面的形式更易于理解但導致我們要進入代碼中四處找尋myIterator函數。最好在它調用的同一個地方那里有迭代函數的邏輯。在本案例中,我們也不會在其他任何地方需要迭代函數,所以我們無損地把它轉化成一個匿名函數。
這是this,但有時this也是那個
當使用JS開始編寫我們的代碼時,我們最普遍的一個麻煩就是this關鍵字的使用。它是一個真正的牽絆。
就象我們之前提及的那樣,在JS中一個函數也是一個對象,而且有時候我們不注意我們正在傳出一個函數。
拿這小段代碼舉個例:
function buttonClicked(){alert('button ' + this.id + ' was clicked');
}
?
var myButton = document.getElementById('someButtonID');
var myButton2 = document.getElementById('someOtherButtonID');
myButton.onclick = buttonClicked;
myButton2.onclick = buttonClicked;
由于buttonClicked函數在任何對象之外被定義,我們也許以為this關鍵字會包含一個window或document對象(假設這些代碼位于一個在瀏覽器中被瀏覽的HTML頁面中間)的引用。
但是當我們運行這段代碼時,我們看到它如愿工作并顯示被點擊按鈕的id。究竟其中發生了什么另我們使每個按鈕的onclick方法包含buttonClicked對象引用,而無論之前那里有什么。現在無論按鈕什么時候被點擊,瀏覽器都會運行一些類似下行的東西:
那畢竟不是這么混亂,是吧?只要你了解有別的對象要進行處理并想在這些對象的事件上,如點擊按鈕,進行什么行動后發生了什么就行了。
var myHelper =
{
formFields: [],
emptyAllFields: function(){for(i=0; i < this.formFields.length; i++){var elementID = this.formFields[i];
var field = document.getElementById(elementID);
field.value = '';
}}};
?
//tell which form fields we want to work with
myHelper.formFields.push('txtName');
myHelper.formFields.push('txtEmail');
myHelper.formFields.push('txtAddress');
?
//clearing the text boxes:
myHelper.emptyAllFields();
?
var clearButton = document.getElementById('btnClear');
clearButton.onclick = myHelper.emptyAllFields;
因此你想:好了,現在我可以在我的頁面上點擊Clear按鈕,那三個文本框就將被清空。然后你嘗試點擊該按鈕后僅得到一個運行時錯誤。該錯誤將涉及(猜猜是什么?)this關鍵字。該問題是this.formFields沒被定義即便this包含了一個到按鈕的引用,那正是現在所發生的。一個快速的解決方案是重寫我們最后一行的代碼。
clearButton.onclick = function(){
myHelper.emptyAllFields();
};
我們創建一個全新的函數,那種方法在協助對象的場景中調用了我們的協助方法。