1.創(chuàng )建高級對象
使用構造函數來(lái)創(chuàng )建對象
構造函數是一個(gè)函數,調用它來(lái)例示并初始化特殊類(lèi)型的對象??梢允褂?nbsp;new 關(guān)鍵字來(lái)調用一個(gè)構造函數。下面給出了使用構造函數的新示例。
var myObject = new Object(); // 創(chuàng )建沒(méi)有屬性的通用對象。
var myBirthday = new Date(1961, 5, 10); // 創(chuàng )建一個(gè) Date 對象。
var myCar = new Car(); // 創(chuàng )建一個(gè)用戶(hù)定義的對象,并初始化其屬性。
通過(guò)構造函數將一個(gè)參數作為特定的 this 關(guān)鍵字的值傳遞給新創(chuàng )建的空對象。然后構造函數負責為新對象執行適應的初始化(創(chuàng )建屬性并給出其初始值)。完成后,構造函數返回它所構造的對象的一個(gè)參數。
編寫(xiě)構造函數
可以使用 new 運算符結合像 Object()、Date() 和 Function() 這樣的預定義的構造函數來(lái)創(chuàng )建對象并對其初始化。面向對象的編程其強有力的特征是定義自定義構造函數以創(chuàng )建腳本中使用的自定義對象的能力。創(chuàng )建了自定義的構造函數,這樣就可以創(chuàng )建具有已定義屬性的對象。下面是自定義函數的示例(注意 this 關(guān)鍵字的使用)。
function Circle (xPoint, yPoint, radius) {
this.x = xPoint; // 圓心的 x 坐標。
this.y = yPoint; // 圓心的 y 坐標。
this.r = radius; // 圓的半徑。
}
調用 Circle 構造函數時(shí),給出圓心點(diǎn)的值和圓的半徑(所有這些元素是完全定義一個(gè)獨特的圓對象所必需的)。結束時(shí) Circle 對象包含三個(gè)屬性。下面是如何例示 Circle 對象。
var aCircle = new Circle(5, 11, 99);
使用原型來(lái)創(chuàng )建對象
在編寫(xiě)構造函數時(shí),可以使用原型對象(它本身是所有構造函數的一個(gè)屬性)的屬性來(lái)創(chuàng )建繼承屬性和共享方法。原型屬性和方法將按引用復制給類(lèi)中的每個(gè)對象,因此它們都具有相同的值??梢栽谝粋€(gè)對象中更改原型屬性的值,新的值將覆蓋默認值,但僅在該實(shí)例中有效。屬于這個(gè)類(lèi)的其他對象不受此更改的影響。下面給出了使用自定義構造函數的示例,Circle(注意 this 關(guān)鍵字的使用)。
Circle.prototype.pi = Math.PI;
function ACirclesArea () {
return this.pi * this.r * this.r; // 計算圓面積的公式為 ?r2。
}
Circle.prototype.area = ACirclesArea; // 計算圓面積的函數現在是 Circle Prototype 對象的一個(gè)方法。
var a = ACircle.area(); // 此為如何在 Circle 對象上調用面積函數。
使用這個(gè)原則,可以給預定義的構造函數(都具有原型對象)定義附加屬性。例如,如果想要能夠刪除字符串的前后空格(與 VBScript 的 Trim 函數類(lèi)似),就可以給 String 原型對象創(chuàng )建自己的方法。
// 增加一個(gè)名為 trim 的函數作為
// String 構造函數的原型對象的一個(gè)方法。
String.prototype.trim = function()
{
// 用正則表達式將前后空格
// 用空字符串替代。
return this.replace(/(^\s*)|(\s*$)/g, "");
}
// 有空格的字符串
var s = " leading and trailing spaces ";
// 顯示 " leading and trailing spaces (35)"
window.alert(s + " (" + s.length + ")");
// 刪除前后空格
s = s.trim();
// 顯示"leading and trailing spaces (27)"
window.alert(s + " (" + s.length + ")");
2.遞歸
遞歸是一種重要的編程技術(shù)。該方法用于讓一個(gè)函數從其內部調用其自身。一個(gè)示例就是計算階乘。0 的階乘被特別地定義為 1。 更大數的階乘是通過(guò)計算 1 * 2 * ...來(lái)求得的,每次增加 1,直至達到要計算其階乘的那個(gè)數。
下面的段落是用文字定義的計算階乘的一個(gè)函數。
“如果這個(gè)數小于零,則拒絕接收。如果不是一個(gè)整數,則將其向下舍入為相鄰的整數。如果這個(gè)數為 0,則其階乘為 1。如果這個(gè)數大于 0,則將其與相鄰較小的數的階乘相乘。”
要計算任何大于 0 的數的階乘,至少需要計算一個(gè)其他數的階乘。用來(lái)實(shí)現這個(gè)功能的函數就是已經(jīng)位于其中的函數;該函數在執行當前的這個(gè)數之前,必須調用它本身來(lái)計算相鄰的較小數的階乘。這就是一個(gè)遞歸示例。
遞歸和迭代(循環(huán))是密切相關(guān)的 ? 能用遞歸處理的算法也都可以采用迭代,反之亦然。確定的算法通??梢杂脦追N方法實(shí)現,您只需選擇最自然貼切的方法,或者您覺(jué)得用起來(lái)最輕松的一種即可。
顯然,這樣有可能會(huì )出現問(wèn)題??梢院苋菀椎貏?chuàng )建一個(gè)遞歸函數,但該函數不能得到一個(gè)確定的結果,并且不能達到一個(gè)終點(diǎn)。這樣的遞歸將導致計算機執行一個(gè)“無(wú)限”循環(huán)。下面就是一個(gè)示例:在計算階乘的文字描述中遺漏了第一條規則(對負數的處理) ,并試圖計算任何負數的階乘。這將導致失敗,因為按順序計算 -24 的階乘時(shí),首先不得不計算 -25 的階乘;然而這樣又不得不計算 -26 的階乘;如此繼續。很明顯,這樣永遠也不會(huì )到達一個(gè)終止點(diǎn)。
因此在設計遞歸函數時(shí)應特別仔細。如果懷疑其中存在著(zhù)無(wú)限遞歸的可能,則可以讓該函數記錄它調用自身的次數。如果該函數調用自身的次數太多,即使您已決定了它應調用多少次,就自動(dòng)退出。
下面仍然是階乘函數,這次是用 JScript 代碼編寫(xiě)的。
// 計算階乘的函數。如果傳遞了
// 無(wú)效的數值(例如小于零),
// 將返回 -1,表明發(fā)生了錯誤。若數值有效,
// 把數值轉換為最相近的整數,并
// 返回階乘。
function factorial(aNumber) {
aNumber = Math.floor(aNumber); // 如果這個(gè)數不是一個(gè)整數,則向下舍入。
if (aNumber < 0) { // 如果這個(gè)數小于 0,拒絕接收。
return -1;
}
if (aNumber == 0) { // 如果為 0,則其階乘為 1。
return 1;
}
else return (aNumber * factorial(aNumber - 1)); // 否則,遞歸直至完成。
}
3.變量范圍
JScript 有兩種變量范圍:全局和局部。如果在任何函數定義之外聲明了一個(gè)變量,則該變量為全局變量,且該變量的值在整個(gè)持續范圍內都可以訪(fǎng)問(wèn)和修改。如果在函數定義內聲明了一個(gè)變量,則該變量為局部變量。每次執行該函數時(shí)都會(huì )創(chuàng )建和破壞該變量;且它不能被該函數外的任何事物訪(fǎng)問(wèn)。
像 C++ 這樣的語(yǔ)言也有“塊范圍”。在這里,任何一對“{}”都定義新的范圍。JScript 不支持塊范圍。
一個(gè)局部變量的名稱(chēng)可以與某個(gè)全局變量的名稱(chēng)相同,但這是完全不同和獨立的兩個(gè)變量。因此,更改一個(gè)變量的值不會(huì )影響另一個(gè)變量的值。在聲明局部變量的函數內,只有該局部變量有意義。
var aCentaur = "a horse with rider,"; // aCentaur 的全局定義。
// JScript 代碼,為簡(jiǎn)潔起見(jiàn)有省略。
function antiquities() // 在這個(gè)函數中聲明了一個(gè)局部 aCentaur 變量。
{
// JScript 代碼,為簡(jiǎn)潔起見(jiàn)有省略。
var aCentaur = "A centaur is probably a mounted Scythian warrior";
// JScript 代碼,為簡(jiǎn)潔起見(jiàn)有省略。
aCentaur += ", misreported; that is, "; // 添加到局部變量。
// JScript 代碼,為簡(jiǎn)潔起見(jiàn)有省略。
} // 函數結束。
var nothinginparticular = antiquities();
aCentaur += " as seen from a distance by a naive innocent.";
/*
在函數內,該變量的值為 "A centaur is probably a mounted Scythian warrior,
misreported; that is, ";在函數外,該變量的值為這句話(huà)的其余部分:
"a horse with rider, as seen from a distance by a naive innocent."
*/
很重要的一點(diǎn)是注意變量是否是在其所屬范圍的開(kāi)始處聲明的。有時(shí)這會(huì )導致意想不到的情況。
tweak();
var aNumber = 100;
function tweak() {
var newThing = 0; // 顯式聲明 newThing 變量。
// 本語(yǔ)句將未定義的變量賦給 newThing,因為已有名為 aNumber 的局部變量。
newThing = aNumber;
//下一條語(yǔ)句將值 42 賦給局部的 aNumber。aNumber = 42;
if (false) {
var aNumber; // 該語(yǔ)句永遠不會(huì )執行。
aNumber = 123; // 該語(yǔ)句永遠不會(huì )執行。
} // 條件語(yǔ)句結束。
} // 該函數定義結束。
當 JScript 運行函數時(shí),首先查找所有的變量聲明,
var someVariable;
并以未定義的初始值創(chuàng )建變量。如果變量被聲明時(shí)有值,
var someVariable = "something";
那么該變量仍以未定義的值初始化,并且只有在運行了聲明行時(shí)才被聲明值取代,假如曾經(jīng)被聲明過(guò)。
JScript 在運行代碼前處理變量聲明,所以聲明是位于一個(gè)條件塊中還是其他某些結構中無(wú)關(guān)緊要。JScript 找到所有的變量后立即運行函數中的代碼。如果變量是在函數中顯式聲明的 ? 也就是說(shuō),如果它出現于賦值表達式的左邊但沒(méi)有用 var 聲明 ? 那么將把它創(chuàng )建為全局變量。
復制、傳遞和比較數據
在 JScript 中,對數據的處理取決于該數據的類(lèi)型。
按值和按引用的比較
Numbers 和 Boolean 類(lèi)型的值 (true 和 false) 是按值來(lái)復制、傳遞和比較的。當按值復制或傳遞時(shí),將在計算機內存中分配一塊空間并將原值復制到其中。然后,即使更改原來(lái)的值,也不會(huì )影響所復制的值(反過(guò)來(lái)也一樣),因為這兩個(gè)值是獨立的實(shí)體。
對象、數組以及函數是按引用來(lái)復制、傳遞和比較的。 當按地址復制或傳遞時(shí),實(shí)際是創(chuàng )建一個(gè)指向原始項的指針,然后就像拷貝一樣來(lái)使用該指針。如果隨后更改原始項,則將同時(shí)更改原始項和復制項(反過(guò)來(lái)也一樣)。實(shí)際上只有一個(gè)實(shí)體;“復本”并不是一個(gè)真正的復本,而只是該數據的又一個(gè)引用。
當按引用比較時(shí),要想比較成功,兩個(gè)變量必須參照完全相同的實(shí)體。例如,兩個(gè)不同的 Array 對象即使包含相同的元素也將比較為不相等。要想比較成功,其中一個(gè)變量必須為另一個(gè)的參考。要想檢查兩個(gè)數組是否包含了相同的元素,比較 toString() 方法的結果。
最后,字符串是按引用復制和傳遞的,但是是按值來(lái)比較的。請注意,假如有兩個(gè) String 對象(用 new String("something") 創(chuàng )建的),按引用比較它們,但是,如果其中一個(gè)或者兩者都是字符串值的話(huà),按值比較它們。
注意 鑒于 ASCII和 ANSI 字符集的構造方法,按序列順序大寫(xiě)字母位于小寫(xiě)字母的前面。例如 "Zoo" 小于 "aardvark"。如果想執行不區分大小寫(xiě)的匹配,可以對兩個(gè)字符串調用 toUpperCase() 或 toLowerCase()。
傳遞參數給函數
按值傳遞一個(gè)參數給函數就是制作該參數的一個(gè)獨立復本,即一個(gè)只存在于該函數內的復本。即使按引用傳遞對象和數組時(shí),如果直接在函數中用新值覆蓋原先的值,在函數外并不反映新值。只有在對象的屬性或者數組的元素改變時(shí),在函數外才可以看出。
例如(使用 IE 對象模式):
// 本代碼段破壞(覆蓋)其參數,所以
// 調用代碼中反映不出變化。
function Clobber(param)
{
// 破壞參數;在調用代碼中
// 看不到。
param = new Object();
param.message = "This will not work";
}
// 本段代碼改變參數的屬性,
// 在調用代碼中可看到屬性改變。
function Update(param)
{
// 改變對象的屬性;
// 可從調用代碼中看到改變。
param.message = "I was changed";
}
// 創(chuàng )建一個(gè)對象,并賦給一個(gè)屬性。
var obj = new Object();
obj.message = "This is the original";
// 調用 Clobber,并輸出 obj.message。注意,它沒(méi)有發(fā)生變化。
Clobber(obj);
window.alert(obj.message); // 仍然顯示 "This is the original"。
// 調用 Update,并輸出 obj.message。注意,它已經(jīng)被改變了。
Update(obj);
window.alert(obj.message); // 顯示 "I was changed"。
檢驗數據
當按值進(jìn)行檢驗時(shí),是比較兩個(gè)截然不同的項以查看它們是否相等。通常,該比較是逐字節進(jìn)行的。當按引用進(jìn)行檢驗時(shí),是看這兩項是否是指向同一個(gè)原始項的指針。如果是,則比較結果是相等;如果不是,即使它們每個(gè)字節都包含完全一樣的值,比較結果也為不相等。
按引用復制和傳遞字符串能節約內存;但是由于在字符串被創(chuàng )建后不能進(jìn)行更改,因此可以按值進(jìn)行比較。這樣可以檢查兩個(gè)字符串是否包含相同的內容,即使它們是完全獨立產(chǎn)生的。
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請
點(diǎn)擊舉報。