創建高級對象
使用構造函數來創建對象
構造函數是一個函數調用它來例示並初始化特殊類型的對象可以使用 new 關鍵字來調用一個構造函數下面給出了使用構造函數的新示例
var myObject = new Object(); // 創建沒有屬性的通用對象
var myBirthday = new Date( ); // 創建一個 Date 對象
var myCar = new Car(); // 創建一個用戶定義的對象並初始化其屬性
通過構造函數將一個參數作為特定的 this 關鍵字的值傳遞給新創建的空對象然後構造函數負責為新對象執行適應的初始化(創建屬性並給出其初始值)完成後構造函數返回它所構造的對象的一個參數
編寫構造函數
可以使用 new 運算符結合像 Object()Date() 和 Function() 這樣的預定義的構造函數來創建對象並對其初始化面向對象的編程其強有力的特征是定義自定義構造函數以創建腳本中使用的自定義對象的能力創建了自定義的構造函數這樣就可以創建具有已定義屬性的對象下面是自定義函數的示例(注意 this 關鍵字的使用)
function Circle (xPoint yPoint radius) {
thisx = xPoint; // 圓心的 x 坐標
thisy = yPoint; // 圓心的 y 坐標
thisr = radius; // 圓的半徑
}
調用 Circle 構造函數時給出圓心點的值和圓的半徑(所有這些元素是完全定義一個獨特的圓對象所必需的)結束時 Circle 對象包含三個屬性下面是如何例示 Circle 對象
var aCirle = new Circle( );
使用原型來創建對象
在編寫構造函數時可以使用原型對象(它本身是所有構造函數的一個屬性)的屬性來創建繼承屬性和共享方法原型屬性和方法將按引用復制給類中的每個對象因此它們都具有相同的值可以在一個對象中 更改原型屬性的值新的值將覆蓋默認值但僅在該實例中有效屬於這個類的其他對象不受此更改的影響下面給出了使用自定義構造函數的示例Circle(注意 this 關鍵字的使用)
Circleprototypepi = MathPI;
function ACirclesArea () {
return thispi * thisr * thisr; // 計算圓面積的公式為 ?r
}
Circleprototypearea = ACirclesArea; // 計算圓面積的函數現在是 Circle Prototype 對象的一個方法
var a = ACirclearea(); // 此為如何在 Circle 對象上調用面積函數
使用這個原則可以給預定義的構造函數(都具有原型對象)定義附加屬性例如如果想要能夠刪除字符串的前後空格(與 VBScript 的 Trim 函數類似)就可以給 String 原型對象創建自己的方法
// 增加一個名為 trim 的函數作為
// String 構造函數的原型對象的一個方法
Stringprototypetrim = function()
{
// 用正則表達式將前後空格
// 用空字符串替代
return thisreplace(/(^\s*) (\s*$)/g );
}
// 有空格的字符串
var s = leading and trailing spaces ;
// 顯示 leading and trailing spaces ()
windowalert(s + ( + slength + ));
// 刪除前後空格
s = strim();
// 顯示leading and trailing spaces ()
windowalert(s + ( + slength + ));
遞歸
遞歸是一種重要的編程技術該方法用於讓一個函數從其內部調用其自身一個示例就是計算階乘 的階乘被特別地定義為 更大數的階乘是通過計算 * * 來求得的每次增加 直至達到要計算其階乘的那個數
下面的段落是用文字定義的計算階乘的一個函數
如果這個數小於零則拒絕接收如果不是一個整數則將其向下捨入為相鄰的整數如果這個數為 則其階乘為 如果這個數大於 則將其與相鄰較小的數的階乘相乘
要計算任何大於 的數的階乘至少需要計算一個其他數的階乘用來實現這個功能的函數就是已經位於其中的函數該函數在執行當前的這個數之前必須調用它本身來計算相鄰的較小數的階乘這就是一個遞歸示例
遞歸和迭代(循環)是密切相關的 ? 能用遞歸處理的算法也都可以采用迭代反之亦然確定的算法通常可以用幾種方法實現您只需選擇最自然貼切的方法或者您覺得用起來最輕松的一種即可
顯然這樣有可能會出現問題可以很容易地創建一個遞歸函數但該函數不能得到一個確定的結果並且不能達到一個終點這樣的遞歸將導致計算機執行一個無限循環下面就是一個示例在計算階乘的文字描述中遺漏了第一條規則(對負數的處理) 並試圖計算任何負數的階乘這將導致失敗因為按順序計算 的階乘時首先不得不計算 的階乘然而這樣又不得不計算 的階乘如此繼續很明顯這樣永遠也不會到達一個終止點
因此在設計遞歸函數時應特別仔細如果懷疑其中存在著無限遞歸的可能則可以讓該函數記錄它調用自身的次數如果該函數調用自身的次數太多即使您已決定了它應調用多少次就自動退出
下面仍然是階乘函數這次是用 JScript 代碼編寫的
// 計算階乘的函數如果傳遞了
// 無效的數值(例如小於零)
// 將返回 表明發生了錯誤若數值有效
// 把數值轉換為最相近的整數並
// 返回階乘
function factorial(aNumber) {
aNumber = Mathfloor(aNumber); // 如果這個數不是一個整數則向下捨入
if (aNumber < ) { // 如果這個數小於 拒絕接收
return ;
}
if (aNumber == ) { // 如果為 則其階乘為
return ;
}
else return (aNumber * factorial(aNumber )); // 否則遞歸直至完成
}
變量范圍
JScript 有兩種變量范圍全局和局部如果在任何函數定義之外聲明了一個變量則該變量為全局變量且該變量的值在整個持續范圍內都可以訪問和修改如果在函數定義內聲明了一個變量則該變量為局部變量每次執行該函數時都會創建和破壞該變量且它不能被該函數外的任何事物訪問
像 C++ 這樣的語言也有塊范圍在這裡任何一對{}都定義新的范圍JScript 不支持塊范圍
一個局部變量的名稱可以與某個全局變量的名稱相同但這是完全不同和獨立的兩個變量因此更改一個變量的值不會影響另一個變量的值在聲明局部變量的函數內只有該局部變量有意義
var aCentaur = a horse with rider; // aCentaur 的全局定義
// JScript 代碼為簡潔起見有省略
function antiquities() // 在這個函數中聲明了一個局部 aCentaur 變量
{
// JScript 代碼為簡潔起見有省略
var aCentaur = A centaur is probably a mounted Scythian warrior;
// JScript 代碼為簡潔起見有省略
aCentaur += misreported; that is ; // 添加到局部變量
// JScript 代碼為簡潔起見有省略
} // 函數結束
var nothinginparticular = antiquities();
aCentaur += as seen from a distance by a naive innocent;
/*
在函數內該變量的值為 A centaur is probably a mounted Scythian warrior
misreported; that is 在函數外該變量的值為這句話的其余部分
a horse with rider as seen from a distance by a naive innocent
*/
很重要的一點是注意變量是否是在其所屬范圍的開始處聲明的有時這會導致意想不到的情況
tweak();
var aNumber = ;
function tweak() {
var newThing = ; // 顯式聲明 newThing 變量
// 本語句將未定義的變量賦給 newThing因為已有名為 aNumber 的局部變量
newThing = aNumber;
//下一條語句將值 賦給局部的 aNumberaNumber = ;
if (false) {
var aNumber; // 該語句永遠不會執行
aNumber = ; // 該語句永遠不會執行
} // 條件語句結束
} // 該函數定義結束
當 JScript 運行函數時首先查找所有的變量聲明
var someVariable;
並以未定義的初始值創建變量如果變量被聲明時有值
var someVariable = something;
那麼該變量仍以未定義的值初始化並且只有在運行了聲明行時才被聲明值取代假如曾經被聲明過
JScript 在運行代碼前處理變量聲明所以聲明是位於一個條件塊中還是其他某些結構中無關緊要JScript 找到所有的變量後立即運行函數中的代碼如果變量是在函數中顯式聲明的 ? 也就是說如果它出現於賦值表達式的左邊但沒有用 var 聲明 ? 那麼將把它創建為全局變量
復制傳遞和比較數據
在 JScript 中對數據的處理取決於該數據的類型
按值和按引用的比較
Numbers 和 Boolean 類型的值 (true 和 false) 是按值來復制傳遞和比較的當按值復制或傳遞時將在計算機內存中分配一塊空間並將原值復制到其中然後即使更改原來的值也不會影響所復制的值(反過來也一樣)因為這兩個值是獨立的實體
對象數組以及函數是按引用來復制傳遞和比較的 當按地址復制或傳遞時實際是創建一個指向原始項的指針然後就像拷貝一樣來使用該指針如果隨後更改原始項則將同時更改原始項和復制項(反過來也一樣)實際上只有一個實體復本並不是一個真正的復本而只是該數據的又一個引用
當按引用比較時要想比較成功兩個變量必須參照完全相同的實體例如兩個不同的 Array 對象即使包含相同的元素也將比較為不相等要想比較成功其中一個變量必須為另一個的參考要想檢查兩個數組是否包含了相同的元素比較 toString() 方法的結果
最後字符串是按引用復制和傳遞的但是是按值來比較的請注意假如有兩個 String 對象(用 new String(something) 創建的)按引用比較它們但是如果其中一個或者兩者都是字符串值的話按值比較它們
注意 鑒於 ASCII和 ANSI 字符集的構造方法按序列順序大寫字母位於小寫字母的前面例如 Zoo 小於 aardvark如果想執行不區分大小寫的匹配可以對兩個字符串調用 toUpperCase() 或 toLowerCase()
傳遞參數給函數
按值傳遞一個參數給函數就是制作該參數的一個獨立復本即一個只存在於該函數內的復本即使按引用傳遞對象和數組時如果直接在函數中用新值覆蓋原先的值在函數外並不反映新值只有在對象的屬性或者數組的元素改變時在函數外才可以看出
例如(使用 IE 對象模式)
// 本代碼段破壞(覆蓋)其參數所以
// 調用代碼中反映不出變化
function Clobber(param)
{
// 破壞參數在調用代碼中
// 看不到
param = new Object();
ssage = This will not work;
}
// 本段代碼改變參數的屬性
// 在調用代碼中可看到屬性改變
function Update(param)
{
// 改變對象的屬性
// 可從調用代碼中看到改變
ssage = I was changed;
}
// 創建一個對象並賦給一個屬性
var obj = new Object();
ssage = This is the original;
// 調用 Clobber並輸出 ssage注意它沒有發生變化
Clobber(obj);
windowalert(ssage); // 仍然顯示 This is the original
// 調用 Update並輸出 ssage注意它已經被改變了
Update(obj);
windowalert(ssage); // 顯示 I was changed
檢驗數據
當按值進行檢驗時是比較兩個截然不同的項以查看它們是否相等通常該比較是逐字節進行的當按引用進行檢驗時是看這兩項是否是指向同一個原始項的指針如果是則比較結果是相等如果不是即使它們每個字節都包含完全一樣的值比較結果也為不相等
按引用復制和傳遞字符串能節約內存但是由於在字符串被創建後不能進行更改因此可以按值進行比較這樣可以檢查兩個字符串是否包含相同的內容即使它們是完全獨立產生的
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19737.html