国产AV88|国产乱妇无码在线观看|国产影院精品在线观看十分钟福利|免费看橹橹网站

javascript-gaojichengx有目錄u

發(fā)布時(shí)間:2024-12-25 | 雜志分類:其他
免費(fèi)制作
更多內(nèi)容

javascript-gaojichengx有目錄u

5.1 Object 類型 83 1 2 3 45 13 67 8 9 1011 12引 用 類 型 本章內(nèi)容? 使用對(duì)象? 創(chuàng)建并操作數(shù)組? 理解基本的 JavaScript 類型? 使用基本類型和基本包裝類型用類型的值(對(duì)象)是引用類型的一個(gè)實(shí)例。在 ECMAScript 中,引用類型是一種數(shù)據(jù)結(jié)構(gòu),用于將數(shù)據(jù)和功能組織在一起。它也常被稱為類,但這種稱呼并不妥當(dāng)。盡管 ECMAScript從技術(shù)上講是一門面向?qū)ο蟮恼Z言,但它不具備傳統(tǒng)的面向?qū)ο笳Z言所支持的類和接口等基本結(jié)構(gòu)。引用類型有時(shí)候也被稱為對(duì)象定義,因?yàn)樗鼈兠枋龅氖且活悓?duì)象所具有的屬性和方法。雖然引用類型與類看起來相似,但它們并不是相同的概念。為避免混淆,本書將不使用類這個(gè)概念。如前所述,對(duì)象是某個(gè)特定引用類型的實(shí)例。新對(duì)象是使用 new 操作符后跟一個(gè)構(gòu)造函數(shù)來創(chuàng)建的。構(gòu)造函數(shù)本身就是一個(gè)函數(shù),只不過該函數(shù)是出于創(chuàng)建新對(duì)象的目的而定義的。請(qǐng)看下面這行代碼:var person = new Object(); 這行代碼創(chuàng)建了 Object 引用類型的一個(gè)新實(shí)例,然后把該實(shí)例保存在了變量 person 中。使用的構(gòu)造函數(shù)是 O... [收起]
[展開]
javascript-gaojichengx有目錄u
粉絲: {{bookData.followerCount}}
文本內(nèi)容
第101頁

5.1 Object 類型 83

1

2

3

4

5

13

6

7

8

9

10

11

12

引 用 類 型

本章內(nèi)容

? 使用對(duì)象

? 創(chuàng)建并操作數(shù)組

? 理解基本的 JavaScript 類型

? 使用基本類型和基本包裝類型

用類型的值(對(duì)象)是引用類型的一個(gè)實(shí)例。在 ECMAScript 中,引用類型是一種數(shù)據(jù)結(jié)構(gòu),

用于將數(shù)據(jù)和功能組織在一起。它也常被稱為類,但這種稱呼并不妥當(dāng)。盡管 ECMAScript

從技術(shù)上講是一門面向?qū)ο蟮恼Z言,但它不具備傳統(tǒng)的面向?qū)ο笳Z言所支持的類和接口等基本結(jié)構(gòu)。引

用類型有時(shí)候也被稱為對(duì)象定義,因?yàn)樗鼈兠枋龅氖且活悓?duì)象所具有的屬性和方法。

雖然引用類型與類看起來相似,但它們并不是相同的概念。為避免混淆,本書將

不使用類這個(gè)概念。

如前所述,對(duì)象是某個(gè)特定引用類型的實(shí)例。新對(duì)象是使用 new 操作符后跟一個(gè)構(gòu)造函數(shù)來創(chuàng)建的。

構(gòu)造函數(shù)本身就是一個(gè)函數(shù),只不過該函數(shù)是出于創(chuàng)建新對(duì)象的目的而定義的。請(qǐng)看下面這行代碼:

var person = new Object();

這行代碼創(chuàng)建了 Object 引用類型的一個(gè)新實(shí)例,然后把該實(shí)例保存在了變量 person 中。使用

的構(gòu)造函數(shù)是 Object,它只為新對(duì)象定義了默認(rèn)的屬性和方法。ECMAScript 提供了很多原生引用類

型(例如 Object),以便開發(fā)人員用以實(shí)現(xiàn)常見的計(jì)算任務(wù)。

5.1 Object 類型

到目前為止,我們看到的大多數(shù)引用類型值都是 Object 類型的實(shí)例;而且,Object 也是

ECMAScript 中使用最多的一個(gè)類型。雖然 Object 的實(shí)例不具備多少功能,但對(duì)于在應(yīng)用程序中存儲(chǔ)

和傳輸數(shù)據(jù)而言,它們確實(shí)是非常理想的選擇。

創(chuàng)建 Object 實(shí)例的方式有兩種。第一種是使用 new 操作符后跟 Object 構(gòu)造函數(shù),如下所示:

var person = new Object();

person.name = \"Nicholas\";

person.age = 29;

ObjectTypeExample01.htm

第 5 章

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第102頁

84 第 5 章 引用類型

另一種方式是使用對(duì)象字面量表示法。對(duì)象字面量是對(duì)象定義的一種簡(jiǎn)寫形式,目的在于簡(jiǎn)化創(chuàng)建

包含大量屬性的對(duì)象的過程。下面這個(gè)例子就使用了對(duì)象字面量語法定義了與前面那個(gè)例子中相同的

person 對(duì)象:

var person = {

name : \"Nicholas\",

age : 29

};

ObjectTypeExample02.htm

在這個(gè)例子中,左邊的花括號(hào)({)表示對(duì)象字面量的開始,因?yàn)樗霈F(xiàn)在了表達(dá)式上下文

(expression context)中。ECMAScript 中的表達(dá)式上下文指的是能夠返回一個(gè)值(表達(dá)式)。賦值操作

符表示后面是一個(gè)值,所以左花括號(hào)在這里表示一個(gè)表達(dá)式的開始。同樣的花括號(hào),如果出現(xiàn)在一個(gè)

語句上下文(statement context)中,例如跟在 if 語句條件的后面,則表示一個(gè)語句塊的開始。

然后,我們定義了 name 屬性,之后是一個(gè)冒號(hào),再后面是這個(gè)屬性的值。在對(duì)象字面量中,使用

逗號(hào)來分隔不同的屬性,因此\"Nicholas\"后面是一個(gè)逗號(hào)。但是,在 age 屬性的值 29 的后面不能添

加逗號(hào),因?yàn)?age 是這個(gè)對(duì)象的最后一個(gè)屬性。在最后一個(gè)屬性后面添加逗號(hào),會(huì)在 IE7 及更早版本和

Opera 中導(dǎo)致錯(cuò)誤。

在使用對(duì)象字面量語法時(shí),屬性名也可以使用字符串,如下面這個(gè)例子所示。

var person = {

\"name\" : \"Nicholas\",

\"age\" : 29,

5 : true

};

這個(gè)例子會(huì)創(chuàng)建一個(gè)對(duì)象,包含三個(gè)屬性:name、age 和 5。但這里的數(shù)值屬性名會(huì)自動(dòng)轉(zhuǎn)換為字

符串。

另外,使用對(duì)象字面量語法時(shí),如果留空其花括號(hào),則可以定義只包含默認(rèn)屬性和方法的對(duì)象,如

下所示:

var person = {}; //與 new Object()相同

person.name = \"Nicholas\";

person.age = 29;

這個(gè)例子與本節(jié)前面的例子是等價(jià)的,只不過看起來似乎有點(diǎn)奇怪。關(guān)于對(duì)象字面量語法,我們推

薦只在考慮對(duì)象屬性名的可讀性時(shí)使用。

在通過對(duì)象字面量定義對(duì)象時(shí),實(shí)際上不會(huì)調(diào)用 Object 構(gòu)造函數(shù)(Firefox 2 及

更早版本會(huì)調(diào)用 Object 構(gòu)造函數(shù);但 Firefox 3 之后就不會(huì)了)。

雖然可以使用前面介紹的任何一種方法來定義對(duì)象,但開發(fā)人員更青睞對(duì)象字面量語法,因?yàn)檫@種

語法要求的代碼量少,而且能夠給人封裝數(shù)據(jù)的感覺。實(shí)際上,對(duì)象字面量也是向函數(shù)傳遞大量可選參

數(shù)的首選方式,例如:

function displayInfo(args) {

var output = \"\";

if (typeof args.name == \"string\"){

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第103頁

5.1 Object 類型 85

1

2

3

4

5

13

6

7

8

9

10

11

12

output += \"Name: \" + args.name + \"\

\";

}

if (typeof args.age == \"number\") {

output += \"Age: \" + args.age + \"\

\";

}

alert(output);

}

displayInfo({

name: \"Nicholas\",

age: 29

});

displayInfo({

name: \"Greg\"

});

ObjectTypeExample04.htm

在這個(gè)例子中,函數(shù) displayInfo()接受一個(gè)名為 args 的參數(shù)。這個(gè)參數(shù)可能帶有一個(gè)名為 name

或 age 的屬性,也可能這兩個(gè)屬性都有或者都沒有。在這個(gè)函數(shù)內(nèi)部,我們通過 typeof 操作符來檢測(cè)

每個(gè)屬性是否存在,然后再基于相應(yīng)的屬性來構(gòu)建一條要顯示的消息。然后,我們調(diào)用了兩次這個(gè)函數(shù),

每次都使用一個(gè)對(duì)象字面量來指定不同的數(shù)據(jù)。這兩次調(diào)用傳遞的參數(shù)雖然不同,但函數(shù)都能正常執(zhí)行。

這種傳遞參數(shù)的模式最適合需要向函數(shù)傳入大量可選參數(shù)的情形。一般來講,命

名參數(shù)雖然容易處理,但在有多個(gè)可選參數(shù)的情況下就會(huì)顯示不夠靈活。最好的做法

是對(duì)那些必需值使用命名參數(shù),而使用對(duì)象字面量來封裝多個(gè)可選參數(shù)。

一般來說,訪問對(duì)象屬性時(shí)使用的都是點(diǎn)表示法,這也是很多面向?qū)ο笳Z言中通用的語法。不過,

在 JavaScript 也可以使用方括號(hào)表示法來訪問對(duì)象的屬性。在使用方括號(hào)語法時(shí),應(yīng)該將要訪問的屬性

以字符串的形式放在方括號(hào)中,如下面的例子所示。

alert(person[\"name\"]); //\"Nicholas\"

alert(person.name); //\"Nicholas\"

從功能上看,這兩種訪問對(duì)象屬性的方法沒有任何區(qū)別。但方括號(hào)語法的主要優(yōu)點(diǎn)是可以通過變量

來訪問屬性,例如:

var propertyName = \"name\";

alert(person[propertyName]); //\"Nicholas\"

如果屬性名中包含會(huì)導(dǎo)致語法錯(cuò)誤的字符,或者屬性名使用的是關(guān)鍵字或保留字,也可以使用方括

號(hào)表示法。例如:

person[\"first name\"] = \"Nicholas\";

由于\"first name\"中包含一個(gè)空格,所以不能使用點(diǎn)表示法來訪問它。然而,屬性名中是可以包

含非字母非數(shù)字的,這時(shí)候就可以使用方括號(hào)表示法來訪問它們。

通常,除非必須使用變量來訪問屬性,否則我們建議使用點(diǎn)表示法。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第104頁

86 第 5 章 引用類型

5.2 Array 類型

除了 Object 之外,Array 類型恐怕是 ECMAScript 中最常用的類型了。而且,ECMAScript 中

的數(shù)組與其他多數(shù)語言中的數(shù)組有著相當(dāng)大的區(qū)別。雖然 ECMAScript 數(shù)組與其他語言中的數(shù)組都是

數(shù)據(jù)的有序列表,但與其他語言不同的是,ECMAScript 數(shù)組的每一項(xiàng)可以保存任何類型的數(shù)據(jù)。也

就是說,可以用數(shù)組的第一個(gè)位置來保存字符串,用第二位置來保存數(shù)值,用第三個(gè)位置來保存對(duì)象,

以此類推。而且,ECMAScript 數(shù)組的大小是可以動(dòng)態(tài)調(diào)整的,即可以隨著數(shù)據(jù)的添加自動(dòng)增長(zhǎng)以容

納新增數(shù)據(jù)。

創(chuàng)建數(shù)組的基本方式有兩種。第一種是使用 Array 構(gòu)造函數(shù),如下面的代碼所示。

var colors = new Array();

如果預(yù)先知道數(shù)組要保存的項(xiàng)目數(shù)量,也可以給構(gòu)造函數(shù)傳遞該數(shù)量,而該數(shù)量會(huì)自動(dòng)變成 length

屬性的值。例如,下面的代碼將創(chuàng)建 length 值為 20 的數(shù)組。

var colors = new Array(20);

也可以向 Array 構(gòu)造函數(shù)傳遞數(shù)組中應(yīng)該包含的項(xiàng)。以下代碼創(chuàng)建了一個(gè)包含 3 個(gè)字符串值的數(shù)組:

var colors = new Array(\"red\", \"blue\", \"green\");

當(dāng)然,給構(gòu)造函數(shù)傳遞一個(gè)值也可以創(chuàng)建數(shù)組。但這時(shí)候問題就復(fù)雜一點(diǎn)了,因?yàn)槿绻麄鬟f的是數(shù)

值,則會(huì)按照該數(shù)值創(chuàng)建包含給定項(xiàng)數(shù)的數(shù)組;而如果傳遞的是其他類型的參數(shù),則會(huì)創(chuàng)建包含那個(gè)值

的只有一項(xiàng)的數(shù)組。下面就兩個(gè)例子:

var colors = new Array(3); // 創(chuàng)建一個(gè)包含 3 項(xiàng)的數(shù)組

var names = new Array(\"Greg\"); // 創(chuàng)建一個(gè)包含 1 項(xiàng),即字符串\"Greg\"的數(shù)組

ArrayTypeExample01.htm

另外,在使用 Array 構(gòu)造函數(shù)時(shí)也可以省略 new 操作符。如下面的例子所示,省略 new 操作符的

結(jié)果相同:

var colors = Array(3); // 創(chuàng)建一個(gè)包含 3 項(xiàng)的數(shù)組

var names = Array(\"Greg\"); // 創(chuàng)建一個(gè)包含 1 項(xiàng),即字符串\"Greg\"的數(shù)組

創(chuàng)建數(shù)組的第二種基本方式是使用數(shù)組字面量表示法。數(shù)組字面量由一對(duì)包含數(shù)組項(xiàng)的方括號(hào)表

示,多個(gè)數(shù)組項(xiàng)之間以逗號(hào)隔開,如下所示:

var colors = [\"red\", \"blue\", \"green\"]; // 創(chuàng)建一個(gè)包含 3 個(gè)字符串的數(shù)組

var names = []; // 創(chuàng)建一個(gè)空數(shù)組

var values = [1,2,]; // 不要這樣!這樣會(huì)創(chuàng)建一個(gè)包含 2 或 3 項(xiàng)的數(shù)組

var options = [,,,,,]; // 不要這樣!這樣會(huì)創(chuàng)建一個(gè)包含 5 或 6 項(xiàng)的數(shù)組

ArrayTypeExample02.htm

以上代碼的第一行創(chuàng)建了一個(gè)包含3個(gè)字符串的數(shù)組。第二行使用一對(duì)空方括號(hào)創(chuàng)建了一個(gè)空數(shù)組。

第三行展示了在數(shù)組字面量的最后一項(xiàng)添加逗號(hào)的結(jié)果:在 IE 中,values 會(huì)成為一個(gè)包含 3 個(gè)項(xiàng)且每

項(xiàng)的值分別為 1、2 和 undefined 的數(shù)組;在其他瀏覽器中,values 會(huì)成為一個(gè)包含 2 項(xiàng)且值分別為

1 和 2 的數(shù)組。原因是 IE8 及之前版本中的 ECMAScript 實(shí)現(xiàn)在數(shù)組字面量方面存在 bug。由于這個(gè) bug

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第105頁

5.2 Array 類型 87

1

2

3

4

5

13

6

7

8

9

10

11

12

導(dǎo)致的另一種情況如最后一行代碼所示,該行代碼可能會(huì)創(chuàng)建包含 5 項(xiàng)的數(shù)組(在 IE9+、Firefox、Opera、

Safari 和 Chrome 中),也可能會(huì)創(chuàng)建包含 6 項(xiàng)的數(shù)組(在 IE8 及更早版本中)。在像這種省略值的情況下,

每一項(xiàng)都將獲得 undefined 值;這個(gè)結(jié)果與調(diào)用 Array 構(gòu)造函數(shù)時(shí)傳遞項(xiàng)數(shù)在邏輯上是相同的。但是

由于 IE 的實(shí)現(xiàn)與其他瀏覽器不一致,因此我們強(qiáng)烈建議不要使用這種語法。

與對(duì)象一樣,在使用數(shù)組字面量表示法時(shí),也不會(huì)調(diào)用 Array 構(gòu)造函數(shù)(Firefox 3

及更早版本除外)。

在讀取和設(shè)置數(shù)組的值時(shí),要使用方括號(hào)并提供相應(yīng)值的基于 0 的數(shù)字索引,如下所示:

var colors = [\"red\", \"blue\", \"green\"]; // 定義一個(gè)字符串?dāng)?shù)組

alert(colors[0]); // 顯示第一項(xiàng)

colors[2] = \"black\"; // 修改第三項(xiàng)

colors[3] = \"brown\"; // 新增第四項(xiàng)

方括號(hào)中的索引表示要訪問的值。如果索引小于數(shù)組中的項(xiàng)數(shù),則返回對(duì)應(yīng)項(xiàng)的值,就像這個(gè)例子

中的 colors[0]會(huì)顯示\"red\"一樣。設(shè)置數(shù)組的值也使用相同的語法,但會(huì)替換指定位置的值。如果設(shè)

置某個(gè)值的索引超過了數(shù)組現(xiàn)有項(xiàng)數(shù),如這個(gè)例子中的 colors[3]所示,數(shù)組就會(huì)自動(dòng)增加到該索引

值加 1 的長(zhǎng)度(就這個(gè)例子而言,索引是 3,因此數(shù)組長(zhǎng)度就是 4)。

數(shù)組的項(xiàng)數(shù)保存在其 length 屬性中,這個(gè)屬性始終會(huì)返回 0 或更大的值,如下面這個(gè)例子所示:

var colors = [\"red\", \"blue\", \"green\"]; // 創(chuàng)建一個(gè)包含 3 個(gè)字符串的數(shù)組

var names = []; // 創(chuàng)建一個(gè)空數(shù)組

alert(colors.length); //3

alert(names.length); //0

數(shù)組的 length 屬性很有特點(diǎn)——它不是只讀的。因此,通過設(shè)置這個(gè)屬性,可以從數(shù)組的末尾移

除項(xiàng)或向數(shù)組中添加新項(xiàng)。請(qǐng)看下面的例子:

var colors = [\"red\", \"blue\", \"green\"]; // 創(chuàng)建一個(gè)包含 3 個(gè)字符串的數(shù)組

colors.length = 2;

alert(colors[2]); //undefined

ArrayTypeExample03.htm

這個(gè)例子中的數(shù)組 colors 一開始有 3 個(gè)值。將其 length 屬性設(shè)置為 2 會(huì)移除最后一項(xiàng)(位置為

2 的那一項(xiàng)),結(jié)果再訪問 colors[2]就會(huì)顯示 undefined 了。如果將其 length 屬性設(shè)置為大于數(shù)組

項(xiàng)數(shù)的值,則新增的每一項(xiàng)都會(huì)取得 undefined 值,如下所示:

var colors = [\"red\", \"blue\", \"green\"]; // 創(chuàng)建一個(gè)包含 3 個(gè)字符串的數(shù)組

colors.length = 4;

alert(colors[3]); //undefined

ArrayTypeExample04.htm

在此,雖然 colors 數(shù)組包含 3 個(gè)項(xiàng),但把它的 length 屬性設(shè)置成了 4。這個(gè)數(shù)組不存在位置 3,

所以訪問這個(gè)位置的值就得到了特殊值 undefined。

利用 length 屬性也可以方便地在數(shù)組末尾添加新項(xiàng),如下所示:

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第106頁

88 第 5 章 引用類型

var colors = [\"red\", \"blue\", \"green\"]; // 創(chuàng)建一個(gè)包含 3 個(gè)字符串的數(shù)組

colors[colors.length] = \"black\"; //(在位置 3)添加一種顏色

colors[colors.length] = \"brown\"; //(在位置 4)再添加一種顏色

ArrayTypeExample05.htm

由于數(shù)組最后一項(xiàng)的索引始終是 length-1,因此下一個(gè)新項(xiàng)的位置就是 length。每當(dāng)在數(shù)組末

尾添加一項(xiàng)后,其 length 屬性都會(huì)自動(dòng)更新以反應(yīng)這一變化。換句話說,上面例子第二行中的

colors[colors.length]為位置 3 添加了一個(gè)值,最后一行的 colors[colors.length]則為位置 4

添加了一個(gè)值。當(dāng)把一個(gè)值放在超出當(dāng)前數(shù)組大小的位置上時(shí),數(shù)組就會(huì)重新計(jì)算其長(zhǎng)度值,即長(zhǎng)度值

等于最后一項(xiàng)的索引加 1,如下面的例子所示:

var colors = [\"red\", \"blue\", \"green\"]; // 創(chuàng)建一個(gè)包含 3 個(gè)字符串的數(shù)組

colors[99] = \"black\"; // (在位置 99)添加一種顏色

alert(colors.length); // 100

ArrayTypeExample06.htm

在這個(gè)例子中,我們向 colors 數(shù)組的位置 99 插入了一個(gè)值,結(jié)果數(shù)組新長(zhǎng)度(length)就是 100

(99+1)。而位置 3 到位置 98 實(shí)際上都是不存在的,所以訪問它們都將返回 undefined。

數(shù)組最多可以包含 4 294 967 295 個(gè)項(xiàng),這幾乎已經(jīng)能夠滿足任何編程需求了。如

果想添加的項(xiàng)數(shù)超過這個(gè)上限值,就會(huì)發(fā)生異常。而創(chuàng)建一個(gè)初始大小與這個(gè)上限值

接近的數(shù)組,則可能會(huì)導(dǎo)致運(yùn)行時(shí)間超長(zhǎng)的腳本錯(cuò)誤。

5.2.1 檢測(cè)數(shù)組

自從 ECMAScript 3 做出規(guī)定以后,就出現(xiàn)了確定某個(gè)對(duì)象是不是數(shù)組的經(jīng)典問題。對(duì)于一個(gè)網(wǎng)頁,

或者一個(gè)全局作用域而言,使用 instanceof 操作符就能得到滿意的結(jié)果:

if (value instanceof Array){

//對(duì)數(shù)組執(zhí)行某些操作

}

instanceof 操作符的問題在于,它假定只有一個(gè)全局執(zhí)行環(huán)境。如果網(wǎng)頁中包含多個(gè)框架,那實(shí)

際上就存在兩個(gè)以上不同的全局執(zhí)行環(huán)境,從而存在兩個(gè)以上不同版本的 Array 構(gòu)造函數(shù)。如果你從

一個(gè)框架向另一個(gè)框架傳入一個(gè)數(shù)組,那么傳入的數(shù)組與在第二個(gè)框架中原生創(chuàng)建的數(shù)組分別具有各自

不同的構(gòu)造函數(shù)。

為了解決這個(gè)問題,ECMAScript 5 新增了 Array.isArray()方法。這個(gè)方法的目的是最終確定某

個(gè)值到底是不是數(shù)組,而不管它是在哪個(gè)全局執(zhí)行環(huán)境中創(chuàng)建的。這個(gè)方法的用法如下。

if (Array.isArray(value)){

//對(duì)數(shù)組執(zhí)行某些操作

}

支持 Array.isArray()方法的瀏覽器有 IE9+、Firefox 4+、Safari 5+、Opera 10.5+和 Chrome。要

在尚未實(shí)現(xiàn)這個(gè)方法中的瀏覽器中準(zhǔn)確檢測(cè)數(shù)組,請(qǐng)參考 22.1.1 節(jié)。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第107頁

5.2 Array 類型 89

1

2

3

4

5

13

6

7

8

9

10

11

12

5.2.2 轉(zhuǎn)換方法

如前所述,所有對(duì)象都具有 toLocaleString()、toString()和 valueOf()方法。其中,調(diào)用

數(shù)組的 toString()方法會(huì)返回由數(shù)組中每個(gè)值的字符串形式拼接而成的一個(gè)以逗號(hào)分隔的字符串。而

調(diào)用 valueOf()返回的還是數(shù)組。實(shí)際上,為了創(chuàng)建這個(gè)字符串會(huì)調(diào)用數(shù)組每一項(xiàng)的 toString()方

法。來看下面這個(gè)例子。

var colors = [\"red\", \"blue\", \"green\"]; // 創(chuàng)建一個(gè)包含 3 個(gè)字符串的數(shù)組

alert(colors.toString()); // red,blue,green

alert(colors.valueOf()); // red,blue,green

alert(colors); // red,blue,green

ArrayTypeExample07.htm

在這里,我們首先顯式地調(diào)用了 toString()方法,以便返回?cái)?shù)組的字符串表示,每個(gè)值的字符串

表示拼接成了一個(gè)字符串,中間以逗號(hào)分隔。接著調(diào)用 valueOf()方法,而最后一行代碼直接將數(shù)組

傳遞給了 alert()。由于 alert()要接收字符串參數(shù),所以它會(huì)在后臺(tái)調(diào)用 toString()方法,由此

會(huì)得到與直接調(diào)用 toString()方法相同的結(jié)果。

另外,toLocaleString()方法經(jīng)常也會(huì)返回與 toString()和 valueOf()方法相同的值,但也

不總是如此。當(dāng)調(diào)用數(shù)組的 toLocaleString()方法時(shí),它也會(huì)創(chuàng)建一個(gè)數(shù)組值的以逗號(hào)分隔的字符

串。而與前兩個(gè)方法唯一的不同之處在于,這一次為了取得每一項(xiàng)的值,調(diào)用的是每一項(xiàng)的 toLocaleString()方法,而不是 toString()方法。請(qǐng)看下面這個(gè)例子。

var person1 = {

toLocaleString : function () {

return \"Nikolaos\";

},

toString : function() {

return \"Nicholas\";

}

};

var person2 = {

toLocaleString : function () {

return \"Grigorios\";

},

toString : function() {

return \"Greg\";

}

};

var people = [person1, person2];

alert(people); //Nicholas,Greg

alert(people.toString()); //Nicholas,Greg

alert(people.toLocaleString()); //Nikolaos,Grigorios

ArrayTypeExample08.htm

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第108頁

90 第 5 章 引用類型

我們?cè)谶@里定義了兩個(gè)對(duì)象:person1 和 person2。而且還分別為每個(gè)對(duì)象定義了一個(gè) toString()

方法和一個(gè) toLocaleString()方法,這兩個(gè)方法返回不同的值。然后,創(chuàng)建一個(gè)包含前面定義的兩

個(gè)對(duì)象的數(shù)組。在將數(shù)組傳遞給 alert()時(shí),輸出結(jié)果是\"Nicholas,Greg\",因?yàn)檎{(diào)用了數(shù)組每一項(xiàng)

的 toString()方法(同樣,這與下一行顯式調(diào)用 toString()方法得到的結(jié)果相同)。而當(dāng)調(diào)用數(shù)組

的 toLocaleString()方法時(shí),輸出結(jié)果是\"Nikolaos,Grigorios\",原因是調(diào)用了數(shù)組每一項(xiàng)的

toLocaleString()方法。

數(shù)組繼承的 toLocaleString()、toString()和 valueOf()方法,在默認(rèn)情況下都會(huì)以逗號(hào)分隔的字

符串的形式返回?cái)?shù)組項(xiàng)。而如果使用 join()方法,則可以使用不同的分隔符來構(gòu)建這個(gè)字符串。join()方

法只接收一個(gè)參數(shù),即用作分隔符的字符串,然后返回包含所有數(shù)組項(xiàng)的字符串。請(qǐng)看下面的例子:

var colors = [\"red\", \"green\", \"blue\"];

alert(colors.join(\",\")); //red,green,blue

alert(colors.join(\"||\")); //red||green||blue

ArrayTypeJoinExample01.htm

在這里,我們使用 join()方法重現(xiàn)了 toString()方法的輸出。在傳遞逗號(hào)的情況下,得到了以

逗號(hào)分隔的數(shù)組值。而在最后一行代碼中,我們傳遞了雙豎線符號(hào),結(jié)果就得到了字符串\"red||

green||blue\"。如果不給 join()方法傳入任何值,或者給它傳入 undefined,則使用逗號(hào)作為分隔

符。IE7 及更早版本會(huì)錯(cuò)誤的使用字符串\"undefined\"作為分隔符。

如果數(shù)組中的某一項(xiàng)的值是 null 或者 undefined,那么該值在 join()、

toLocaleString()、toString()和 valueOf()方法返回的結(jié)果中以空字符串表示。

5.2.3 棧方法

ECMAScript 數(shù)組也提供了一種讓數(shù)組的行為類似于其他數(shù)據(jù)結(jié)構(gòu)的方法。具體說來,數(shù)組可以表

現(xiàn)得就像棧一樣,后者是一種可以限制插入和刪除項(xiàng)的數(shù)據(jù)結(jié)構(gòu)。棧是一種 LIFO(Last-In-First-Out,

后進(jìn)先出)的數(shù)據(jù)結(jié)構(gòu),也就是最新添加的項(xiàng)最早被移除。而棧中項(xiàng)的插入(叫做推入)和移除(叫做

彈出),只發(fā)生在一個(gè)位置——棧的頂部。ECMAScript 為數(shù)組專門提供了 push()和 pop()方法,以便

實(shí)現(xiàn)類似棧的行為。

push()方法可以接收任意數(shù)量的參數(shù),把它們逐個(gè)添加到數(shù)組末尾,并返回修改后數(shù)組的長(zhǎng)度。而

pop()方法則從數(shù)組末尾移除最后一項(xiàng),減少數(shù)組的 length 值,然后返回移除的項(xiàng)。請(qǐng)看下面的例子:

var colors = new Array(); // 創(chuàng)建一個(gè)數(shù)組

var count = colors.push(\"red\", \"green\"); // 推入兩項(xiàng)

alert(count); //2

count = colors.push(\"black\"); // 推入另一項(xiàng)

alert(count); //3

var item = colors.pop(); // 取得最后一項(xiàng)

alert(item); //\"black\"

alert(colors.length); //2

ArrayTypeExample09.htm

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第109頁

5.2 Array 類型 91

1

2

3

4

5

13

6

7

8

9

10

11

12

以上代碼中的數(shù)組可以看成是棧(代碼本身沒有任何區(qū)別,而 push()和 pop()都是數(shù)組默認(rèn)的方

法)。首先,我們使用 push()將兩個(gè)字符串推入數(shù)組的末尾,并將返回的結(jié)果保存在變量 count 中(值

為 2)。然后,再推入一個(gè)值,而結(jié)果仍然保存在 count 中。因?yàn)榇藭r(shí)數(shù)組中包含 3 項(xiàng),所以 push()

返回 3。在調(diào)用 pop()時(shí),它會(huì)返回?cái)?shù)組的最后一項(xiàng),即字符串\"black\"。此后,數(shù)組中僅剩兩項(xiàng)。

可以將棧方法與其他數(shù)組方法連用,像下面這個(gè)例子一樣。

var colors = [\"red\", \"blue\"];

colors.push(\"brown\"); // 添加另一項(xiàng)

colors[3] = \"black\"; // 添加一項(xiàng)

alert(colors.length); // 4

var item = colors.pop(); // 取得最后一項(xiàng)

alert(item); //\"black\"

ArrayTypeExample10.htm

在此,我們首先用兩個(gè)值來初始化一個(gè)數(shù)組。然后,使用 push()添加第三個(gè)值,再通過直接在位

置 3 上賦值來添加第四個(gè)值。而在調(diào)用 pop()時(shí),該方法返回了字符串\"black\",即最后一個(gè)添加到數(shù)

組的值。

5.2.4 隊(duì)列方法

棧數(shù)據(jù)結(jié)構(gòu)的訪問規(guī)則是 LIFO(后進(jìn)先出),而隊(duì)列數(shù)據(jù)結(jié)構(gòu)的訪問規(guī)則是 FIFO(First-In-First-Out,

先進(jìn)先出)。隊(duì)列在列表的末端添加項(xiàng),從列表的前端移除項(xiàng)。由于 push()是向數(shù)組末端添加項(xiàng)的方法,

因此要模擬隊(duì)列只需一個(gè)從數(shù)組前端取得項(xiàng)的方法。實(shí)現(xiàn)這一操作的數(shù)組方法就是 shift(),它能夠移

除數(shù)組中的第一個(gè)項(xiàng)并返回該項(xiàng),同時(shí)將數(shù)組長(zhǎng)度減 1。結(jié)合使用 shift()和 push()方法,可以像使

用隊(duì)列一樣使用數(shù)組。

var colors = new Array(); //創(chuàng)建一個(gè)數(shù)組

var count = colors.push(\"red\", \"green\"); //推入兩項(xiàng)

alert(count); //2

count = colors.push(\"black\"); //推入另一項(xiàng)

alert(count); //3

var item = colors.shift(); //取得第一項(xiàng)

alert(item); //\"red\"

alert(colors.length); //2

ArrayTypeExample11.htm

這個(gè)例子首先使用 push()方法創(chuàng)建了一個(gè)包含 3 種顏色名稱的數(shù)組。代碼中加粗的那一行使用

shift()方法從數(shù)組中取得了第一項(xiàng),即\"red\"。在移除第一項(xiàng)之后,\"green\"就變成了第一項(xiàng),而

\"black\"則變成了第二項(xiàng),數(shù)組也只包含兩項(xiàng)了。

ECMAScript 還為數(shù)組提供了一個(gè) unshift()方法。顧名思義,unshift()與 shift()的用途相反:

它能在數(shù)組前端添加任意個(gè)項(xiàng)并返回新數(shù)組的長(zhǎng)度。因此,同時(shí)使用 unshift()和 pop()方法,可以

從相反的方向來模擬隊(duì)列,即在數(shù)組的前端添加項(xiàng),從數(shù)組末端移除項(xiàng),如下面的例子所示。

var colors = new Array(); //創(chuàng)建一個(gè)數(shù)組

var count = colors.unshift(\"red\", \"green\"); //推入兩項(xiàng)

alert(count); //2

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第110頁

92 第 5 章 引用類型

count = colors.unshift(\"black\"); //推入另一項(xiàng)

alert(count); //3

var item = colors.pop(); //取得最后一項(xiàng)

alert(item); //\"green\"

alert(colors.length); //2

ArrayTypeExample12.htm

這個(gè)例子創(chuàng)建了一個(gè)數(shù)組并使用 unshift()方法先后推入了 3 個(gè)值。首先是\"red\"和\"green\",然

后是\"black\",數(shù)組中各項(xiàng)的順序?yàn)?#92;"black\"、\"red\"、\"green\"。在調(diào)用 pop()方法時(shí),移除并返回

的是最后一項(xiàng),即\"green\"。

IE7 及更早版本對(duì) JavaScript 的實(shí)現(xiàn)中存在一個(gè)偏差,其 unshift()方法總是返

回 undefined 而不是數(shù)組的新長(zhǎng)度。IE8 在非兼容模式下會(huì)返回正確的長(zhǎng)度值。

5.2.5 重排序方法

數(shù)組中已經(jīng)存在兩個(gè)可以直接用來重排序的方法:reverse()和 sort()。有讀者可能猜到了,

reverse()方法會(huì)反轉(zhuǎn)數(shù)組項(xiàng)的順序。請(qǐng)看下面這個(gè)例子。

var values = [1, 2, 3, 4, 5];

values.reverse();

alert(values); //5,4,3,2,1

ArrayTypeExample13.htm

這里數(shù)組的初始值及順序是 1、2、3、4、5。而調(diào)用數(shù)組的 reverse()方法后,其值的順序變成了

5、4、3、2、1。這個(gè)方法的作用相當(dāng)直觀明了,但不夠靈活,因此才有了 sort()方法。

在默認(rèn)情況下,sort()方法按升序排列數(shù)組項(xiàng)——即最小的值位于最前面,最大的值排在最后面。

為了實(shí)現(xiàn)排序,sort()方法會(huì)調(diào)用每個(gè)數(shù)組項(xiàng)的 toString()轉(zhuǎn)型方法,然后比較得到的字符串,以

確定如何排序。即使數(shù)組中的每一項(xiàng)都是數(shù)值,sort()方法比較的也是字符串,如下所示。

var values = [0, 1, 5, 10, 15];

values.sort();

alert(values); //0,1,10,15,5

ArrayTypeExample14.htm

可見,即使例子中值的順序沒有問題,但 sort()方法也會(huì)根據(jù)測(cè)試字符串的結(jié)果改變?cè)瓉淼捻樞颉?/p>

因?yàn)閿?shù)值 5 雖然小于 10,但在進(jìn)行字符串比較時(shí),\"10\"則位于\"5\"的前面,于是數(shù)組的順序就被修改了。

不用說,這種排序方式在很多情況下都不是最佳方案。因此 sort()方法可以接收一個(gè)比較函數(shù)作為參

數(shù),以便我們指定哪個(gè)值位于哪個(gè)值的前面。

比較函數(shù)接收兩個(gè)參數(shù),如果第一個(gè)參數(shù)應(yīng)該位于第二個(gè)之前則返回一個(gè)負(fù)數(shù),如果兩個(gè)參數(shù)相等

則返回 0,如果第一個(gè)參數(shù)應(yīng)該位于第二個(gè)之后則返回一個(gè)正數(shù)。以下就是一個(gè)簡(jiǎn)單的比較函數(shù):

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第111頁

5.2 Array 類型 93

1

2

3

4

5

13

6

7

8

9

10

11

12

function compare(value1, value2) {

if (value1 < value2) {

return -1;

} else if (value1 > value2) {

return 1;

} else {

return 0;

}

}

ArrayTypeExample15.htm

這個(gè)比較函數(shù)可以適用于大多數(shù)數(shù)據(jù)類型,只要將其作為參數(shù)傳遞給 sort()方法即可,如下面這

個(gè)例子所示。

var values = [0, 1, 5, 10, 15];

values.sort(compare);

alert(values); //0,1,5,10,15

在將比較函數(shù)傳遞到 sort()方法之后,數(shù)值仍然保持了正確的升序。當(dāng)然,也可以通過比較函數(shù)

產(chǎn)生降序排序的結(jié)果,只要交換比較函數(shù)返回的值即可。

function compare(value1, value2) {

if (value1 < value2) {

return 1;

} else if (value1 > value2) {

return -1;

} else {

return 0;

}

}

var values = [0, 1, 5, 10, 15];

values.sort(compare);

alert(values); // 15,10,5,1,0

ArrayTypeExample16.htm

在這個(gè)修改后的例子中,比較函數(shù)在第一個(gè)值應(yīng)該位于第二個(gè)之后的情況下返回 1,而在第一個(gè)值

應(yīng)該在第二個(gè)之前的情況下返回?1。交換返回值的意思是讓更大的值排位更靠前,也就是對(duì)數(shù)組按照降

序排序。當(dāng)然,如果只想反轉(zhuǎn)數(shù)組原來的順序,使用 reverse()方法要更快一些。

reverse()和 sort()方法的返回值是經(jīng)過排序之后的數(shù)組。

對(duì)于數(shù)值類型或者其 valueOf()方法會(huì)返回?cái)?shù)值類型的對(duì)象類型,可以使用一個(gè)更簡(jiǎn)單的比較函

數(shù)。這個(gè)函數(shù)只要用第二個(gè)值減第一個(gè)值即可。

function compare(value1, value2){

return value2 - value1;

}

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第112頁

94 第 5 章 引用類型

由于比較函數(shù)通過返回一個(gè)小于零、等于零或大于零的值來影響排序結(jié)果,因此減法操作就可以適

當(dāng)?shù)靥幚硭羞@些情況。

5.2.6 操作方法

ECMAScript 為操作已經(jīng)包含在數(shù)組中的項(xiàng)提供了很多方法。其中,concat()方法可以基于當(dāng)前數(shù)

組中的所有項(xiàng)創(chuàng)建一個(gè)新數(shù)組。具體來說,這個(gè)方法會(huì)先創(chuàng)建當(dāng)前數(shù)組一個(gè)副本,然后將接收到的參數(shù)

添加到這個(gè)副本的末尾,最后返回新構(gòu)建的數(shù)組。在沒有給 concat()方法傳遞參數(shù)的情況下,它只是

復(fù)制當(dāng)前數(shù)組并返回副本。如果傳遞給 concat()方法的是一或多個(gè)數(shù)組,則該方法會(huì)將這些數(shù)組中的

每一項(xiàng)都添加到結(jié)果數(shù)組中。如果傳遞的值不是數(shù)組,這些值就會(huì)被簡(jiǎn)單地添加到結(jié)果數(shù)組的末尾。下

面來看一個(gè)例子。

var colors = [\"red\", \"green\", \"blue\"];

var colors2 = colors.concat(\"yellow\", [\"black\", \"brown\"]);

alert(colors); //red,green,blue

alert(colors2); //red,green,blue,yellow,black,brown

ArrayTypeConcatExample01.htm

以上代碼開始定義了一個(gè)包含 3 個(gè)值的數(shù)組 colors。然后,基于 colors 調(diào)用了 concat()方法,

并傳入字符串\"yellow\"和一個(gè)包含\"black\"和\"brown\"的數(shù)組。最終,結(jié)果數(shù)組 colors2 中包含了

\"red\"、\"green\"、\"blue\"、\"yellow\"、\"black\"和\"brown\"。至于原來的數(shù)組 colors,其值仍然

保持不變。

下一個(gè)方法是 slice(),它能夠基于當(dāng)前數(shù)組中的一或多個(gè)項(xiàng)創(chuàng)建一個(gè)新數(shù)組。slice()方法可以

接受一或兩個(gè)參數(shù),即要返回項(xiàng)的起始和結(jié)束位置。在只有一個(gè)參數(shù)的情況下,slice()方法返回從該

參數(shù)指定位置開始到當(dāng)前數(shù)組末尾的所有項(xiàng)。如果有兩個(gè)參數(shù),該方法返回起始和結(jié)束位置之間的項(xiàng)—

—但不包括結(jié)束位置的項(xiàng)。注意,slice()方法不會(huì)影響原始數(shù)組。請(qǐng)看下面的例子。

var colors = [\"red\", \"green\", \"blue\", \"yellow\", \"purple\"];

var colors2 = colors.slice(1);

var colors3 = colors.slice(1,4);

alert(colors2); //green,blue,yellow,purple

alert(colors3); //green,blue,yellow

ArrayTypeSliceExample01.htm

在這個(gè)例子中,開始定義的數(shù)組 colors 包含 5 項(xiàng)。調(diào)用 slice()并傳入 1 會(huì)得到一個(gè)包含 4 項(xiàng)的

新數(shù)組;因?yàn)槭菑奈恢?1 開始復(fù)制,所以會(huì)包含\"green\"而不會(huì)包含\"red\"。這個(gè)新數(shù)組 colors2 中

包含的是\"green\"、\"blue\"、\"yellow\"和\"purple\"。接著,我們?cè)俅握{(diào)用 slice()并傳入了 1 和 4,

表示復(fù)制從位置 1 開始,到位置 3 結(jié)束。結(jié)果數(shù)組 colors3 中包含了\"green\"、\"blue\"和\"yellow\"。

如果 slice()方法的參數(shù)中有一個(gè)負(fù)數(shù),則用數(shù)組長(zhǎng)度加上該數(shù)來確定相應(yīng)的位

置。例如,在一個(gè)包含 5 項(xiàng)的數(shù)組上調(diào)用 slice(-2,-1)與調(diào)用 slice(3,4)得到的

結(jié)果相同。如果結(jié)束位置小于起始位置,則返回空數(shù)組。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第113頁

5.2 Array 類型 95

1

2

3

4

5

13

6

7

8

9

10

11

12

下面我們來介紹 splice()方法,這個(gè)方法恐怕要算是最強(qiáng)大的數(shù)組方法了,它有很多種用法。

splice()的主要用途是向數(shù)組的中部插入項(xiàng),但使用這種方法的方式則有如下 3 種。

? 刪除:可以刪除任意數(shù)量的項(xiàng),只需指定 2 個(gè)參數(shù):要?jiǎng)h除的第一項(xiàng)的位置和要?jiǎng)h除的項(xiàng)數(shù)。

例如,splice(0,2)會(huì)刪除數(shù)組中的前兩項(xiàng)。

? 插入:可以向指定位置插入任意數(shù)量的項(xiàng),只需提供 3 個(gè)參數(shù):起始位置、0(要?jiǎng)h除的項(xiàng)數(shù))

和要插入的項(xiàng)。如果要插入多個(gè)項(xiàng),可以再傳入第四、第五,以至任意多個(gè)項(xiàng)。例如,

splice(2,0,\"red\",\"green\")會(huì)從當(dāng)前數(shù)組的位置 2 開始插入字符串\"red\"和\"green\"。

? 替換:可以向指定位置插入任意數(shù)量的項(xiàng),且同時(shí)刪除任意數(shù)量的項(xiàng),只需指定 3 個(gè)參數(shù):起

始位置、要?jiǎng)h除的項(xiàng)數(shù)和要插入的任意數(shù)量的項(xiàng)。插入的項(xiàng)數(shù)不必與刪除的項(xiàng)數(shù)相等。例如,

splice (2,1,\"red\",\"green\")會(huì)刪除當(dāng)前數(shù)組位置 2 的項(xiàng),然后再從位置 2 開始插入字符串

\"red\"和\"green\"。

splice()方法始終都會(huì)返回一個(gè)數(shù)組,該數(shù)組中包含從原始數(shù)組中刪除的項(xiàng)(如果沒有刪除任何

項(xiàng),則返回一個(gè)空數(shù)組)。下面的代碼展示了上述 3 種使用 splice()方法的方式。

var colors = [\"red\", \"green\", \"blue\"];

var removed = colors.splice(0,1); // 刪除第一項(xiàng)

alert(colors); // green,blue

alert(removed); // red,返回的數(shù)組中只包含一項(xiàng)

removed = colors.splice(1, 0, \"yellow\", \"orange\"); // 從位置 1 開始插入兩項(xiàng)

alert(colors); // green,yellow,orange,blue

alert(removed); // 返回的是一個(gè)空數(shù)組

removed = colors.splice(1, 1, \"red\", \"purple\"); // 插入兩項(xiàng),刪除一項(xiàng)

alert(colors); // green,red,purple,orange,blue

alert(removed); // yellow,返回的數(shù)組中只包含一項(xiàng)

ArrayTypeSpliceExample01.htm

上面的例子首先定義了一個(gè)包含 3項(xiàng)的數(shù)組 colors。第一次調(diào)用 splice()方法只是刪除了這個(gè)數(shù)組的

第一項(xiàng),之后 colors 還包含\"green\"和\"blue\"兩項(xiàng)。第二次調(diào)用 splice()方法時(shí)在位置 1插入了兩項(xiàng),

結(jié)果 colors 中包含\"green\"、\"yellow\"、\"orange\"和\"blue\"。這一次操作沒有刪除項(xiàng),因此返回了一個(gè)

空數(shù)組。最后一次調(diào)用 splice()方法刪除了位置 1處的一項(xiàng),然后又插入了\"red\"和\"purple\"。在完成以

上操作之后,數(shù)組 colors 中包含的是\"green\"、\"red\"、\"purple\"、\"orange\"和\"blue\"。

5.2.7 位置方法

ECMAScript 5 為數(shù)組實(shí)例添加了兩個(gè)位置方法:indexOf()和 lastIndexOf()。這兩個(gè)方法都接收

兩個(gè)參數(shù):要查找的項(xiàng)和(可選的)表示查找起點(diǎn)位置的索引。其中,indexOf()方法從數(shù)組的開頭(位

置 0)開始向后查找,lastIndexOf()方法則從數(shù)組的末尾開始向前查找。

這兩個(gè)方法都返回要查找的項(xiàng)在數(shù)組中的位置,或者在沒找到的情況下返回?1。在比較第一個(gè)參數(shù)

與數(shù)組中的每一項(xiàng)時(shí),會(huì)使用全等操作符;也就是說,要求查找的項(xiàng)必須嚴(yán)格相等(就像使用===一樣)。

以下是幾個(gè)例子。

var numbers = [1,2,3,4,5,4,3,2,1];

alert(numbers.indexOf(4)); //3

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第114頁

96 第 5 章 引用類型

alert(numbers.lastIndexOf(4)); //5

alert(numbers.indexOf(4, 4)); //5

alert(numbers.lastIndexOf(4, 4)); //3

var person = { name: \"Nicholas\" };

var people = [{ name: \"Nicholas\" }];

var morePeople = [person];

alert(people.indexOf(person)); //-1

alert(morePeople.indexOf(person)); //0

ArrayIndexOfExample01.htm

使用 indexOf()和 lastIndexOf()方法查找特定項(xiàng)在數(shù)組中的位置非常簡(jiǎn)單,支持它們的瀏覽器包

括 IE9+、Firefox 2+、Safari 3+、Opera 9.5+和 Chrome。

5.2.8 迭代方法

ECMAScript 5 為數(shù)組定義了 5 個(gè)迭代方法。每個(gè)方法都接收兩個(gè)參數(shù):要在每一項(xiàng)上運(yùn)行的函數(shù)和

(可選的)運(yùn)行該函數(shù)的作用域?qū)ο蟆绊?this 的值。傳入這些方法中的函數(shù)會(huì)接收三個(gè)參數(shù):數(shù)

組項(xiàng)的值、該項(xiàng)在數(shù)組中的位置和數(shù)組對(duì)象本身。根據(jù)使用的方法不同,這個(gè)函數(shù)執(zhí)行后的返回值可能

會(huì)也可能不會(huì)影響方法的返回值。以下是這 5 個(gè)迭代方法的作用。

? every():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),如果該函數(shù)對(duì)每一項(xiàng)都返回 true,則返回 true。

? filter():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),返回該函數(shù)會(huì)返回 true 的項(xiàng)組成的數(shù)組。

? forEach():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù)。這個(gè)方法沒有返回值。

? map():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),返回每次函數(shù)調(diào)用的結(jié)果組成的數(shù)組。

? some():對(duì)數(shù)組中的每一項(xiàng)運(yùn)行給定函數(shù),如果該函數(shù)對(duì)任一項(xiàng)返回 true,則返回 true。

以上方法都不會(huì)修改數(shù)組中的包含的值。

在這些方法中,最相似的是 every()和 some(),它們都用于查詢數(shù)組中的項(xiàng)是否滿足某個(gè)條件。

對(duì) every()來說,傳入的函數(shù)必須對(duì)每一項(xiàng)都返回 true,這個(gè)方法才返回 true;否則,它就返回

false。而 some()方法則是只要傳入的函數(shù)對(duì)數(shù)組中的某一項(xiàng)返回 true,就會(huì)返回 true。請(qǐng)看以下

例子。

var numbers = [1,2,3,4,5,4,3,2,1];

var everyResult = numbers.every(function(item, index, array){

return (item > 2);

});

alert(everyResult); //false

var someResult = numbers.some(function(item, index, array){

return (item > 2);

});

alert(someResult); //true

ArrayEveryAndSomeExample01.htm

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第115頁

5.2 Array 類型 97

1

2

3

4

5

13

6

7

8

9

10

11

12

以上代碼調(diào)用了 every()和 some(),傳入的函數(shù)只要給定項(xiàng)大于 2就會(huì)返回 true。對(duì)于 every(),

它返回的是 false,因?yàn)橹挥胁糠謹(jǐn)?shù)組項(xiàng)符合條件。對(duì)于 some(),結(jié)果就是 true,因?yàn)橹辽儆幸豁?xiàng)

是大于 2 的。

下面再看一看 filter()函數(shù),它利用指定的函數(shù)確定是否在返回的數(shù)組中包含某一項(xiàng)。例如,要

返回一個(gè)所有數(shù)值都大于 2 的數(shù)組,可以使用以下代碼。

var numbers = [1,2,3,4,5,4,3,2,1];

var filterResult = numbers.filter(function(item, index, array){

return (item > 2);

});

alert(filterResult); //[3,4,5,4,3]

ArrayFilterExample01.htm

這里,通過調(diào)用 filter()方法創(chuàng)建并返回了包含 3、4、5、4、3 的數(shù)組,因?yàn)閭魅氲暮瘮?shù)對(duì)它們

每一項(xiàng)都返回 true。這個(gè)方法對(duì)查詢符合某些條件的所有數(shù)組項(xiàng)非常有用。

map()也返回一個(gè)數(shù)組,而這個(gè)數(shù)組的每一項(xiàng)都是在原始數(shù)組中的對(duì)應(yīng)項(xiàng)上運(yùn)行傳入函數(shù)的結(jié)果。

例如,可以給數(shù)組中的每一項(xiàng)乘以 2,然后返回這些乘積組成的數(shù)組,如下所示。

var numbers = [1,2,3,4,5,4,3,2,1];

var mapResult = numbers.map(function(item, index, array){

return item * 2;

});

alert(mapResult); //[2,4,6,8,10,8,6,4,2]

ArrayMapExample01.htm

以上代碼返回的數(shù)組中包含給每個(gè)數(shù)乘以 2 之后的結(jié)果。這個(gè)方法適合創(chuàng)建包含的項(xiàng)與另一個(gè)數(shù)組

一一對(duì)應(yīng)的數(shù)組。

最后一個(gè)方法是 forEach(),它只是對(duì)數(shù)組中的每一項(xiàng)運(yùn)行傳入的函數(shù)。這個(gè)方法沒有返回值,

本質(zhì)上與使用 for 循環(huán)迭代數(shù)組一樣。來看一個(gè)例子。

var numbers = [1,2,3,4,5,4,3,2,1];

numbers.forEach(function(item, index, array){

//執(zhí)行某些操作

});

這些數(shù)組方法通過執(zhí)行不同的操作,可以大大方便處理數(shù)組的任務(wù)。支持這些迭代方法的瀏覽器有

IE9+、Firefox 2+、Safari 3+、Opera 9.5+和 Chrome。

5.2.9 歸并方法

ECMAScript 5 還新增了兩個(gè)歸并數(shù)組的方法:reduce()和 reduceRight()。這兩個(gè)方法都會(huì)迭

代數(shù)組的所有項(xiàng),然后構(gòu)建一個(gè)最終返回的值。其中,reduce()方法從數(shù)組的第一項(xiàng)開始,逐個(gè)遍歷

到最后。而 reduceRight()則從數(shù)組的最后一項(xiàng)開始,向前遍歷到第一項(xiàng)。

這兩個(gè)方法都接收兩個(gè)參數(shù):一個(gè)在每一項(xiàng)上調(diào)用的函數(shù)和(可選的)作為歸并基礎(chǔ)的初始值。傳

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第116頁

98 第 5 章 引用類型

給 reduce()和 reduceRight()的函數(shù)接收 4 個(gè)參數(shù):前一個(gè)值、當(dāng)前值、項(xiàng)的索引和數(shù)組對(duì)象。這

個(gè)函數(shù)返回的任何值都會(huì)作為第一個(gè)參數(shù)自動(dòng)傳給下一項(xiàng)。第一次迭代發(fā)生在數(shù)組的第二項(xiàng)上,因此第

一個(gè)參數(shù)是數(shù)組的第一項(xiàng),第二個(gè)參數(shù)就是數(shù)組的第二項(xiàng)。

使用 reduce()方法可以執(zhí)行求數(shù)組中所有值之和的操作,比如:

var values = [1,2,3,4,5];

var sum = values.reduce(function(prev, cur, index, array){

return prev + cur;

});

alert(sum); //15

ArrayReductionExample01.htm

第一次執(zhí)行回調(diào)函數(shù),prev 是 1,cur 是 2。第二次,prev 是 3(1 加 2 的結(jié)果),cur 是 3(數(shù)組

的第三項(xiàng))。這個(gè)過程會(huì)持續(xù)到把數(shù)組中的每一項(xiàng)都訪問一遍,最后返回結(jié)果。

reduceRight()的作用類似,只不過方向相反而已。來看下面這個(gè)例子。

var values = [1,2,3,4,5];

var sum = values.reduceRight(function(prev, cur, index, array){

return prev + cur;

});

alert(sum); //15

在這個(gè)例子中,第一次執(zhí)行回調(diào)函數(shù),prev 是 5,cur 是 4。當(dāng)然,最終結(jié)果相同,因?yàn)閳?zhí)行的都

是簡(jiǎn)單相加的操作。

使用 reduce()還是 reduceRight(),主要取決于要從哪頭開始遍歷數(shù)組。除此之外,它們完全

相同。

支持這兩個(gè)歸并函數(shù)的瀏覽器有 IE9+、Firefox 3+、Safari 4+、Opera 10.5 和 Chrome。

5.3 Date 類型

ECMAScript 中的 Date 類型是在早期 Java 中的 java.util.Date 類基礎(chǔ)上構(gòu)建的。為此,Date

類型使用自 UTC(Coordinated Universal Time,國際協(xié)調(diào)時(shí)間)1970 年 1 月 1 日午夜(零時(shí))開始經(jīng)過

的毫秒數(shù)來保存日期。在使用這種數(shù)據(jù)存儲(chǔ)格式的條件下,Date 類型保存的日期能夠精確到 1970 年 1

月 1 日之前或之后的 285 616 年。

要?jiǎng)?chuàng)建一個(gè)日期對(duì)象,使用 new 操作符和 Date 構(gòu)造函數(shù)即可,如下所示。

var now = new Date();

DateTypeExample01.htm

在調(diào)用 Date 構(gòu)造函數(shù)而不傳遞參數(shù)的情況下,新創(chuàng)建的對(duì)象自動(dòng)獲得當(dāng)前日期和時(shí)間。如果想根

據(jù)特定的日期和時(shí)間創(chuàng)建日期對(duì)象,必須傳入表示該日期的毫秒數(shù)(即從 UTC 時(shí)間 1970 年 1 月 1 日午

夜起至該日期止經(jīng)過的毫秒數(shù))。為了簡(jiǎn)化這一計(jì)算過程,ECMAScript 提供了兩個(gè)方法:Date.parse()

和 Date.UTC()。

其中,Date.parse()方法接收一個(gè)表示日期的字符串參數(shù),然后嘗試根據(jù)這個(gè)字符串返回相應(yīng)日

期的毫秒數(shù)。ECMA-262 沒有定義 Date.parse()應(yīng)該支持哪種日期格式,因此這個(gè)方法的行為因?qū)崿F(xiàn)

而異,而且通常是因地區(qū)而異。將地區(qū)設(shè)置為美國的瀏覽器通常都接受下列日期格式:

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第117頁

5.3 Date 類型 99

1

2

3

4

5

13

6

7

8

9

10

11

12

? “月/日/年”,如 6/13/2004;

? “英文月名 日,年”,如 January 12,2004;

? “英文星期幾 英文月名 日 年 時(shí):分:秒 時(shí)區(qū)”,如 Tue May 25 2004 00:00:00 GMT-0700。

? ISO 8601 擴(kuò)展格式 YYYY-MM-DDTHH:mm:ss.sssZ(例如 2004-05-25T00:00:00)。只有兼容

ECMAScript 5 的實(shí)現(xiàn)支持這種格式。

例如,要為 2004 年 5 月 25 日創(chuàng)建一個(gè)日期對(duì)象,可以使用下面的代碼:

var someDate = new Date(Date.parse(\"May 25, 2004\"));

DateTypeExample01.htm

如果傳入 Date.parse()方法的字符串不能表示日期,那么它會(huì)返回 NaN。實(shí)際上,如果直接將表

示日期的字符串傳遞給 Date 構(gòu)造函數(shù),也會(huì)在后臺(tái)調(diào)用 Date.parse()。換句話說,下面的代碼與前

面的例子是等價(jià)的:

var someDate = new Date(\"May 25, 2004\");

這行代碼將會(huì)得到與前面相同的日期對(duì)象。

日期對(duì)象及其在不同瀏覽器中的實(shí)現(xiàn)有許多奇怪的行為。其中有一種傾向是將超

出范圍的值替換成當(dāng)前的值,以便生成輸出。例如,在解析\"January 32, 2007\"

時(shí),有的瀏覽器會(huì)將其解釋為\"February 1, 2007\"。而 Opera 則傾向于插入當(dāng)前月

份的當(dāng)前日期,返回\"January 當(dāng)前日期,2007\"。也就是說,如果在 2007 年 9 月

21 日運(yùn)行前面的代碼,將會(huì)得到\"January 21, 2007\"(都是 21 日)。

Date.UTC()方法同樣也返回表示日期的毫秒數(shù),但它與 Date.parse()在構(gòu)建值時(shí)使用不同的信

息。Date.UTC()的參數(shù)分別是年份、基于 0 的月份(一月是 0,二月是 1,以此類推)、月中的哪一天

(1 到 31)、小時(shí)數(shù)(0 到 23)、分鐘、秒以及毫秒數(shù)。在這些參數(shù)中,只有前兩個(gè)參數(shù)(年和月)是必

需的。如果沒有提供月中的天數(shù),則假設(shè)天數(shù)為 1;如果省略其他參數(shù),則統(tǒng)統(tǒng)假設(shè)為 0。以下是兩個(gè)

使用 Date.UTC()方法的例子:

// GMT 時(shí)間 2000 年 1 月 1 日午夜零時(shí)

var y2k = new Date(Date.UTC(2000, 0));

// GMT 時(shí)間 2005 年 5 月 5 日下午 5:55:55

var allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));

DateTypeUTCExample01.htm

這個(gè)例子創(chuàng)建了兩個(gè)日期對(duì)象。第一個(gè)對(duì)象表示 GMT 時(shí)間 2000 年 1 月 1 日午夜零時(shí),傳入的值一

個(gè)是表示年份的 2000,一個(gè)是表示月份的 0(即一月份)。因?yàn)槠渌麉?shù)是自動(dòng)填充的(即月中的天數(shù)

為 1,其他所有參數(shù)均為 0),所以結(jié)果就是該月第一天的午夜零時(shí)。第二個(gè)對(duì)象表示 GMT 時(shí)間 2005

年 5 月 5 日下午 5:55:55,即使日期和時(shí)間中只包含 5,也需要傳入不一樣的參數(shù):月份必須是 4(因?yàn)?/p>

月份是基于 0 的)、小時(shí)必須設(shè)置為 17(因?yàn)樾r(shí)以 0 到 23 表示),剩下的參數(shù)就很直觀了。

如同模仿 Date.parse()一樣,Date 構(gòu)造函數(shù)也會(huì)模仿 Date.UTC(),但有一點(diǎn)明顯不同:日期

和時(shí)間都基于本地時(shí)區(qū)而非 GMT 來創(chuàng)建。不過,Date 構(gòu)造函數(shù)接收的參數(shù)仍然與 Date.UTC()相同。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第118頁

100 第 5 章 引用類型

因此,如果第一個(gè)參數(shù)是數(shù)值,Date 構(gòu)造函數(shù)就會(huì)假設(shè)該值是日期中的年份,而第二個(gè)參數(shù)是月份,

以此類推。據(jù)此,可以將前面的例子重寫如下。

// 本地時(shí)間 2000 年 1 月 1 日午夜零時(shí)

var y2k = new Date(2000, 0);

// 本地時(shí)間 2005 年 5 月 5 日下午 5:55:55

var allFives = new Date(2005, 4, 5, 17, 55, 55);

DateTypeConstructorExample01.htm

以上代碼創(chuàng)建了與前面例子中相同的兩個(gè)日期對(duì)象,只不過這次的日期都是基于系統(tǒng)設(shè)置的本地時(shí)

區(qū)創(chuàng)建的。

ECMAScript 5 添加了 Data.now()方法,返回表示調(diào)用這個(gè)方法時(shí)的日期和時(shí)間的毫秒數(shù)。這個(gè)方

法簡(jiǎn)化了使用 Data 對(duì)象分析代碼的工作。例如:

//取得開始時(shí)間

var start = Date.now();

//調(diào)用函數(shù)

doSomething();

//取得停止時(shí)間

var stop = Date.now(),

result = stop – start;

支持 Data.now()方法的瀏覽器包括 IE9+、Firefox 3+、Safari 3+、Opera 10.5 和 Chrome。在不支

持它的瀏覽器中,使用+操作符把 Data 對(duì)象轉(zhuǎn)換成字符串,也可以達(dá)到同樣的目的。

//取得開始時(shí)間

var start = +new Date();

//調(diào)用函數(shù)

doSomething();

//取得停止時(shí)間

var stop = +new Date(),

result = stop - start;

5.3.1 繼承的方法

與其他引用類型一樣,Date 類型也重寫了 toLocaleString()、toString()和 valueOf()方法;

但這些方法返回的值與其他類型中的方法不同。Date 類型的 toLocaleString()方法會(huì)按照與瀏覽器

設(shè)置的地區(qū)相適應(yīng)的格式返回日期和時(shí)間。這大致意味著時(shí)間格式中會(huì)包含 AM 或 PM,但不會(huì)包含時(shí)

區(qū)信息(當(dāng)然,具體的格式會(huì)因?yàn)g覽器而異)。而 toString()方法則通常返回帶有時(shí)區(qū)信息的日期和

時(shí)間,其中時(shí)間一般以軍用時(shí)間(即小時(shí)的范圍是 0 到 23)表示。下面給出了在不同瀏覽器中調(diào)用

toLocaleString()和 toString()方法,輸出 PST(Pacific Standard Time,太平洋標(biāo)準(zhǔn)時(shí)間)時(shí)間 2007

年 2 月 1 日午夜零時(shí)的結(jié)果。

Internet Explorer 8

toLocaleString() — Thursday, February 01, 2007 12:00:00 AM

toString() — Thu Feb 1 00:00:00 PST 2007

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第119頁

5.3 Date 類型 101

1

2

3

4

5

13

6

7

8

9

10

11

12

Firefox 3.5

toLocaleString() — Thursday, February 01, 2007 12:00:00 AM

toString() — Thu Feb 01 2007 00:00:00 GMT-0800 (Pacific Standard Time)

Safari 4

toLocaleString() — Thursday, February 01, 2007 00:00:00

toString() — Thu Feb 01 2007 00:00:00 GMT-0800 (Pacific Standard Time)

Chrome 4

toLocaleString() — Thu Feb 01 2007 00:00:00 GMT-0800 (Pacific Standard Time)

toString() — Thu Feb 01 2007 00:00:00 GMT-0800 (Pacific Standard Time)

Opera 10

toLocaleString() — 2/1/2007 12:00:00 AM

toString() — Thu, 01 Feb 2007 00:00:00 GMT-0800

顯然,這兩個(gè)方法在不同的瀏覽器中返回的日期和時(shí)間格式可謂大相徑庭。事實(shí)上,

toLocaleString()和 toString()的這一差別僅在調(diào)試代碼時(shí)比較有用,而在顯示日期和時(shí)間時(shí)沒有

什么價(jià)值。

至于 Date 類型的 valueOf()方法,則根本不返回字符串,而是返回日期的毫秒表示。因此,可以

方便使用比較操作符(小于或大于)來比較日期值。請(qǐng)看下面的例子。

var date1 = new Date(2007, 0, 1); //\"January 1, 2007\"

var date2 = new Date(2007, 1, 1); //\"February 1, 2007\"

alert(date1 < date2); //true

alert(date1 > date2); //false

DateTypeValueOfExample01.htm

從邏輯上講,2007 年 1 月 1 日要早于 2007 年 2 月 1 日,此時(shí)如果我們說前者小于后者比較符合常

理。而表示 2007 年 1 月 1 日的毫秒值小于表示 2007 年 2 月 1 日的毫秒值,因此在首先使用小于操作符

比較日期時(shí),返回的結(jié)果是 true。這樣,就為我們比較日期提供了極大方便。

5.3.2 日期格式化方法

Date 類型還有一些專門用于將日期格式化為字符串的方法,這些方法如下。

? toDateString()——以特定于實(shí)現(xiàn)的格式顯示星期幾、月、日和年;

? toTimeString()——以特定于實(shí)現(xiàn)的格式顯示時(shí)、分、秒和時(shí)區(qū);

? toLocaleDateString()——以特定于地區(qū)的格式顯示星期幾、月、日和年;

? toLocaleTimeString()——以特定于實(shí)現(xiàn)的格式顯示時(shí)、分、秒;

? toUTCString()——以特定于實(shí)現(xiàn)的格式完整的 UTC 日期。

與 toLocaleString()和 toString()方法一樣,以上這些字符串格式方法的輸出也是因?yàn)g覽器

而異的,因此沒有哪一個(gè)方法能夠用來在用戶界面中顯示一致的日期信息。

除了前面介紹的方法之外,還有一個(gè)名叫 toGMTString()的方法,這是一個(gè)與

toUTCString()等價(jià)的方法,其存在目的在于確保向后兼容。不過,ECMAScript 推

薦現(xiàn)在編寫的代碼一律使用 toUTCString()方法。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第120頁

102 第 5 章 引用類型

5.3.3 日期/時(shí)間組件方法

到目前為止,剩下還未介紹的 Date 類型的方法(如下表所示),都是直接取得和設(shè)置日期值中特

定部分的方法了。需要注意的是,UTC 日期指的是在沒有時(shí)區(qū)偏差的情況下(將日期轉(zhuǎn)換為 GMT 時(shí)間)

的日期值。

方 法 說 明

getTime() 返回表示日期的毫秒數(shù);與valueOf()方法返回的值相同

setTime(毫秒) 以毫秒數(shù)設(shè)置日期,會(huì)改變整個(gè)日期

getFullYear() 取得4位數(shù)的年份(如2007而非僅07)

getUTCFullYear() 返回UTC日期的4位數(shù)年份

setFullYear(年) 設(shè)置日期的年份。傳入的年份值必須是4位數(shù)字(如2007而非僅07)

setUTCFullYear(年) 設(shè)置UTC日期的年份。傳入的年份值必須是4位數(shù)字(如2007而非僅07)

getMonth() 返回日期中的月份,其中0表示一月,11表示十二月

getUTCMonth() 返回UTC日期中的月份,其中0表示一月,11表示十二月

setMonth(月) 設(shè)置日期的月份。傳入的月份值必須大于0,超過11則增加年份

setUTCMonth(月) 設(shè)置UTC日期的月份。傳入的月份值必須大于0,超過11則增加年份

getDate() 返回日期月份中的天數(shù)(1到31)

getUTCDate() 返回UTC日期月份中的天數(shù)(1到31)

setDate(日) 設(shè)置日期月份中的天數(shù)。如果傳入的值超過了該月中應(yīng)有的天數(shù),則增加月份

setUTCDate(日) 設(shè)置UTC日期月份中的天數(shù)。如果傳入的值超過了該月中應(yīng)有的天數(shù),則增加月份

getDay() 返回日期中星期的星期幾(其中0表示星期日,6表示星期六)

getUTCDay() 返回UTC日期中星期的星期幾(其中0表示星期日,6表示星期六)

getHours() 返回日期中的小時(shí)數(shù)(0到23)

getUTCHours() 返回UTC日期中的小時(shí)數(shù)(0到23)

setHours(時(shí)) 設(shè)置日期中的小時(shí)數(shù)。傳入的值超過了23則增加月份中的天數(shù)

setUTCHours(時(shí)) 設(shè)置UTC日期中的小時(shí)數(shù)。傳入的值超過了23則增加月份中的天數(shù)

getMinutes() 返回日期中的分鐘數(shù)(0到59)

getUTCMinutes() 返回UTC日期中的分鐘數(shù)(0到59)

setMinutes(分) 設(shè)置日期中的分鐘數(shù)。傳入的值超過59則增加小時(shí)數(shù)

setUTCMinutes(分) 設(shè)置UTC日期中的分鐘數(shù)。傳入的值超過59則增加小時(shí)數(shù)

getSeconds() 返回日期中的秒數(shù)(0到59)

getUTCSeconds() 返回UTC日期中的秒數(shù)(0到59)

setSeconds(秒) 設(shè)置日期中的秒數(shù)。傳入的值超過了59會(huì)增加分鐘數(shù)

setUTCSeconds(秒) 設(shè)置UTC日期中的秒數(shù)。傳入的值超過了59會(huì)增加分鐘數(shù)

getMilliseconds() 返回日期中的毫秒數(shù)

getUTCMilliseconds() 返回UTC日期中的毫秒數(shù)

setMilliseconds(毫秒) 設(shè)置日期中的毫秒數(shù)

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第121頁

5.4 RegExp 類型 103

1

2

3

4

5

13

6

7

8

9

10

11

12

(續(xù))

方 法 說 明

setUTCMilliseconds(毫秒) 設(shè)置UTC日期中的毫秒數(shù)

getTimezoneOffset() 返回本地時(shí)間與UTC時(shí)間相差的分鐘數(shù)。例如,美國東部標(biāo)準(zhǔn)時(shí)間返回300。在某

地進(jìn)入夏令時(shí)的情況下,這個(gè)值會(huì)有所變化

5.4 RegExp 類型

ECMAScript 通過 RegExp 類型來支持正則表達(dá)式。使用下面類似 Perl 的語法,就可以創(chuàng)建一個(gè)正

則表達(dá)式。

var expression = / pattern / flags ;

其中的模式(pattern)部分可以是任何簡(jiǎn)單或復(fù)雜的正則表達(dá)式,可以包含字符類、限定符、分組、

向前查找以及反向引用。每個(gè)正則表達(dá)式都可帶有一或多個(gè)標(biāo)志(flags),用以標(biāo)明正則表達(dá)式的行為。

正則表達(dá)式的匹配模式支持下列 3 個(gè)標(biāo)志。

? g:表示全局(global)模式,即模式將被應(yīng)用于所有字符串,而非在發(fā)現(xiàn)第一個(gè)匹配項(xiàng)時(shí)立即

停止;

? i:表示不區(qū)分大小寫(case-insensitive)模式,即在確定匹配項(xiàng)時(shí)忽略模式與字符串的大小寫;

? m:表示多行(multiline)模式,即在到達(dá)一行文本末尾時(shí)還會(huì)繼續(xù)查找下一行中是否存在與模

式匹配的項(xiàng)。

因此,一個(gè)正則表達(dá)式就是一個(gè)模式與上述 3 個(gè)標(biāo)志的組合體。不同組合產(chǎn)生不同結(jié)果,如下面的

例子所示。

/*

* 匹配字符串中所有\"at\"的實(shí)例

*/

var pattern1 = /at/g;

/*

* 匹配第一個(gè)\"bat\"或\"cat\",不區(qū)分大小寫

*/

var pattern2 = /[bc]at/i;

/*

* 匹配所有以\"at\"結(jié)尾的 3 個(gè)字符的組合,不區(qū)分大小寫

*/

var pattern3 = /.at/gi;

與其他語言中的正則表達(dá)式類似,模式中使用的所有元字符都必須轉(zhuǎn)義。正則表達(dá)式中的元字符包括:

( [ { \\ ^ $ | ) ? * + .]}

這些元字符在正則表達(dá)式中都有一或多種特殊用途,因此如果想要匹配字符串中包含的這些字符,

就必須對(duì)它們進(jìn)行轉(zhuǎn)義。下面給出幾個(gè)例子。

/*

* 匹配第一個(gè)\"bat\"或\"cat\",不區(qū)分大小寫

*/

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第122頁

104 第 5 章 引用類型

var pattern1 = /[bc]at/i;

/*

* 匹配第一個(gè)\" [bc]at\",不區(qū)分大小寫

*/

var pattern2 = /\\[bc\\]at/i;

/*

* 匹配所有以\"at\"結(jié)尾的 3 個(gè)字符的組合,不區(qū)分大小寫

*/

var pattern3 = /.at/gi;

/*

* 匹配所有\".at\",不區(qū)分大小寫

*/

var pattern4 = /\\.at/gi;

在上面的例子中,pattern1 匹配第一個(gè)\"bat\"或\"cat\",不區(qū)分大小寫。而要想直接匹配\"[bc]at\"

的話,就需要像定義 pattern2 一樣,對(duì)其中的兩個(gè)方括號(hào)進(jìn)行轉(zhuǎn)義。對(duì)于 pattern3 來說,句點(diǎn)表示

位于\"at\"之前的任意一個(gè)可以構(gòu)成匹配項(xiàng)的字符。但如果想匹配\".at\",則必須對(duì)句點(diǎn)本身進(jìn)行轉(zhuǎn)義,

如 pattern4 所示。

前面舉的這些例子都是以字面量形式來定義的正則表達(dá)式。另一種創(chuàng)建正則表達(dá)式的方式是使用

RegExp 構(gòu)造函數(shù),它接收兩個(gè)參數(shù):一個(gè)是要匹配的字符串模式,另一個(gè)是可選的標(biāo)志字符串。可以

使用字面量定義的任何表達(dá)式,都可以使用構(gòu)造函數(shù)來定義,如下面的例子所示。

/*

* 匹配第一個(gè)\"bat\"或\"cat\",不區(qū)分大小寫

*/

var pattern1 = /[bc]at/i;

/*

* 與 pattern1 相同,只不過是使用構(gòu)造函數(shù)創(chuàng)建的

*/

var pattern2 = new RegExp(\"[bc]at\", \"i\");

在此,pattern1 和 pattern2 是兩個(gè)完全等價(jià)的正則表達(dá)式。要注意的是,傳遞給 RegExp 構(gòu)造

函數(shù)的兩個(gè)參數(shù)都是字符串(不能把正則表達(dá)式字面量傳遞給 RegExp 構(gòu)造函數(shù))。由于 RegExp 構(gòu)造

函數(shù)的模式參數(shù)是字符串,所以在某些情況下要對(duì)字符進(jìn)行雙重轉(zhuǎn)義。所有元字符都必須雙重轉(zhuǎn)義,那

些已經(jīng)轉(zhuǎn)義過的字符也是如此,例如\

(字符\\在字符串中通常被轉(zhuǎn)義為\\\\,而在正則表達(dá)式字符串中就

會(huì)變成\\\\\\\\)。下表給出了一些模式,左邊是這些模式的字面量形式,右邊是使用 RegExp 構(gòu)造函數(shù)定義

相同模式時(shí)使用的字符串。

字面量模式 等價(jià)的字符串

/\\[bc\\]at/ \"\\\\[bc\\\\]at\"

/\\.at/ \"\\\\.at\"

/name\\/age/ \"name\\\\/age\"

/\\d.\\d{1,2}/ \"\\\\d.\\\\d{1,2}\"

/\\w\\\\hello\\\\123/ \"\\\\w\\\\\\\\hello\\\\\\\\123\"

使用正則表達(dá)式字面量和使用 RegExp 構(gòu)造函數(shù)創(chuàng)建的正則表達(dá)式不一樣。在 ECMAScript 3 中,

正則表達(dá)式字面量始終會(huì)共享同一個(gè) RegExp 實(shí)例,而使用構(gòu)造函數(shù)創(chuàng)建的每一個(gè)新 RegExp 實(shí)例都是

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第123頁

5.4 RegExp 類型 105

1

2

3

4

5

13

6

7

8

9

10

11

12

一個(gè)新實(shí)例。來看下面的例子。

var re = null,

i;

for (i=0; i < 10; i++){

re = /cat/g;

re.test(\"catastrophe\");

}

for (i=0; i < 10; i++){

re = new RegExp(\"cat\", \"g\");

re.test(\"catastrophe\");

}

在第一個(gè)循環(huán)中,即使是循環(huán)體中指定的,但實(shí)際上只為/cat/創(chuàng)建了一個(gè) RegExp 實(shí)例。由于實(shí)

例屬性(下一節(jié)介紹實(shí)例屬性)不會(huì)重置,所以在循環(huán)中再次調(diào)用 test()方法會(huì)失敗。這是因?yàn)榈谝?/p>

次調(diào)用 test()找到了\"cat\",但第二次調(diào)用是從索引為 3 的字符(上一次匹配的末尾)開始的,所以

就找不到它了。由于會(huì)測(cè)試到字符串末尾,所以下一次再調(diào)用 test()就又從開頭開始了。

第二個(gè)循環(huán)使用 RegExp 構(gòu)造函數(shù)在每次循環(huán)中創(chuàng)建正則表達(dá)式。因?yàn)槊看蔚紩?huì)創(chuàng)建一個(gè)新的

RegExp 實(shí)例,所以每次調(diào)用 test()都會(huì)返回 true。

ECMAScript 5 明確規(guī)定,使用正則表達(dá)式字面量必須像直接調(diào)用 RegExp 構(gòu)造函數(shù)一樣,每次都創(chuàng)

建新的 RegExp 實(shí)例。IE9+、Firefox 4+和 Chrome 都據(jù)此做出了修改。

5.4.1 RegExp實(shí)例屬性

RegExp 的每個(gè)實(shí)例都具有下列屬性,通過這些屬性可以取得有關(guān)模式的各種信息。

? global:布爾值,表示是否設(shè)置了 g 標(biāo)志。

? ignoreCase:布爾值,表示是否設(shè)置了 i 標(biāo)志。

? lastIndex:整數(shù),表示開始搜索下一個(gè)匹配項(xiàng)的字符位置,從 0 算起。

? multiline:布爾值,表示是否設(shè)置了 m 標(biāo)志。

? source:正則表達(dá)式的字符串表示,按照字面量形式而非傳入構(gòu)造函數(shù)中的字符串模式返回。

通過這些屬性可以獲知一個(gè)正則表達(dá)式的各方面信息,但卻沒有多大用處,因?yàn)檫@些信息全都包含

在模式聲明中。例如:

var pattern1 = /\\[bc\\]at/i;

alert(pattern1.global); //false

alert(pattern1.ignoreCase); //true

alert(pattern1.multiline); //false

alert(pattern1.lastIndex); //0

alert(pattern1.source); //\"\\[bc\\]at\"

var pattern2 = new RegExp(\"\\\\[bc\\\\]at\", \"i\");

alert(pattern2.global); //false

alert(pattern2.ignoreCase); //true

alert(pattern2.multiline); //false

alert(pattern2.lastIndex); //0

alert(pattern2.source); //\"\\[bc\\]at\"

RegExpInstancePropertiesExample01.htm

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第124頁

106 第 5 章 引用類型

我們注意到,盡管第一個(gè)模式使用的是字面量,第二個(gè)模式使用了 RegExp 構(gòu)造函數(shù),但它們的

source 屬性是相同的??梢?,source 屬性保存的是規(guī)范形式的字符串,即字面量形式所用的字符串。

5.4.2 RegExp實(shí)例方法

RegExp 對(duì)象的主要方法是 exec(),該方法是專門為捕獲組而設(shè)計(jì)的。exec()接受一個(gè)參數(shù),即

要應(yīng)用模式的字符串,然后返回包含第一個(gè)匹配項(xiàng)信息的數(shù)組;或者在沒有匹配項(xiàng)的情況下返回 null。

返回的數(shù)組雖然是 Array 的實(shí)例,但包含兩個(gè)額外的屬性:index 和 input。其中,index 表示匹配

項(xiàng)在字符串中的位置,而 input 表示應(yīng)用正則表達(dá)式的字符串。在數(shù)組中,第一項(xiàng)是與整個(gè)模式匹配

的字符串,其他項(xiàng)是與模式中的捕獲組匹配的字符串(如果模式中沒有捕獲組,則該數(shù)組只包含一項(xiàng))。

請(qǐng)看下面的例子。

var text = \"mom and dad and baby\";

var pattern = /mom( and dad( and baby)?)?/gi;

var matches = pattern.exec(text);

alert(matches.index); // 0

alert(matches.input); // \"mom and dad and baby\"

alert(matches[0]); // \"mom and dad and baby\"

alert(matches[1]); // \" and dad and baby\"

alert(matches[2]); // \" and baby\"

RegExpExecExample01.htm

這個(gè)例子中的模式包含兩個(gè)捕獲組。最內(nèi)部的捕獲組匹配\"and baby\",而包含它的捕獲組匹配\"and

dad\"或者\"and dad and baby\"。當(dāng)把字符串傳入 exec()方法中之后,發(fā)現(xiàn)了一個(gè)匹配項(xiàng)。因?yàn)檎麄€(gè)

字符串本身與模式匹配,所以返回的數(shù)組 matchs 的 index 屬性值為 0。數(shù)組中的第一項(xiàng)是匹配的整個(gè)

字符串,第二項(xiàng)包含與第一個(gè)捕獲組匹配的內(nèi)容,第三項(xiàng)包含與第二個(gè)捕獲組匹配的內(nèi)容。

對(duì)于 exec()方法而言,即使在模式中設(shè)置了全局標(biāo)志(g),它每次也只會(huì)返回一個(gè)匹配項(xiàng)。在不

設(shè)置全局標(biāo)志的情況下,在同一個(gè)字符串上多次調(diào)用 exec()將始終返回第一個(gè)匹配項(xiàng)的信息。而在設(shè)

置全局標(biāo)志的情況下,每次調(diào)用 exec()則都會(huì)在字符串中繼續(xù)查找新匹配項(xiàng),如下面的例子所示。

var text = \"cat, bat, sat, fat\";

var pattern1 = /.at/;

var matches = pattern1.exec(text);

alert(matches.index); //0

alert(matches[0]); //cat

alert(pattern1.lastIndex); //0

matches = pattern1.exec(text);

alert(matches.index); //0

alert(matches[0]); //cat

alert(pattern1.lastIndex); //0

var pattern2 = /.at/g;

var matches = pattern2.exec(text);

alert(matches.index); //0

alert(matches[0]); //cat

alert(pattern2.lastIndex); //3

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第125頁

5.4 RegExp 類型 107

1

2

3

4

5

13

6

7

8

9

10

11

12

matches = pattern2.exec(text);

alert(matches.index); //5

alert(matches[0]); //bat

alert(pattern2.lastIndex); //8

RegExpExecExample02.htm

這個(gè)例子中的第一個(gè)模式 pattern1 不是全局模式,因此每次調(diào)用 exec()返回的都是第一個(gè)匹配

項(xiàng)(\"cat\")。而第二個(gè)模式 pattern2 是全局模式,因此每次調(diào)用 exec()都會(huì)返回字符串中的下一個(gè)

匹配項(xiàng),直至搜索到字符串末尾為止。此外,還應(yīng)該注意模式的 lastIndex 屬性的變化情況。在全局

匹配模式下,lastIndex 的值在每次調(diào)用 exec()后都會(huì)增加,而在非全局模式下則始終保持不變。

IE 的 JavaScript 實(shí)現(xiàn)在 lastIndex 屬性上存在偏差,即使在非全局模式下,

lastIndex 屬性每次也會(huì)變化。

正則表達(dá)式的第二個(gè)方法是 test(),它接受一個(gè)字符串參數(shù)。在模式與該參數(shù)匹配的情況下返回

true;否則,返回 false。在只想知道目標(biāo)字符串與某個(gè)模式是否匹配,但不需要知道其文本內(nèi)容的

情況下,使用這個(gè)方法非常方便。因此,test()方法經(jīng)常被用在 if 語句中,如下面的例子所示。

var text = \"000-00-0000\";

var pattern = /\\d{3}-\\d{2}-\\d{4}/;

if (pattern.test(text)){

alert(\"The pattern was matched.\");

}

在這個(gè)例子中,我們使用正則表達(dá)式來測(cè)試了一個(gè)數(shù)字序列。如果輸入的文本與模式匹配,則顯示

一條消息。這種用法經(jīng)常出現(xiàn)在驗(yàn)證用戶輸入的情況下,因?yàn)槲覀冎幌胫垒斎胧遣皇怯行В劣谒鼮?/p>

什么無效就無關(guān)緊要了。

RegExp 實(shí)例繼承的 toLocaleString()和 toString()方法都會(huì)返回正則表達(dá)式的字面量,與創(chuàng)

建正則表達(dá)式的方式無關(guān)。例如:

var pattern = new RegExp(\"\\\\[bc\\\\]at\", \"gi\");

alert(pattern.toString()); // /\\[bc\\]at/gi

alert(pattern.toLocaleString()); // /\\[bc\\]at/gi

RegExpToStringExample01.htm

即使上例中的模式是通過調(diào)用 RegExp 構(gòu)造函數(shù)創(chuàng)建的,但 toLocaleString()和 toString()

方法仍然會(huì)像它是以字面量形式創(chuàng)建的一樣顯示其字符串表示。

正則表達(dá)式的 valueOf()方法返回正則表達(dá)式本身。

5.4.3 RegExp構(gòu)造函數(shù)屬性

RegExp 構(gòu)造函數(shù)包含一些屬性(這些屬性在其他語言中被看成是靜態(tài)屬性)。這些屬性適用于作用

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第126頁

108 第 5 章 引用類型

域中的所有正則表達(dá)式,并且基于所執(zhí)行的最近一次正則表達(dá)式操作而變化。關(guān)于這些屬性的另一個(gè)獨(dú)

特之處,就是可以通過兩種方式訪問它們。換句話說,這些屬性分別有一個(gè)長(zhǎng)屬性名和一個(gè)短屬性名

(Opera 是例外,它不支持短屬性名)。下表列出了 RegExp 構(gòu)造函數(shù)的屬性。

長(zhǎng)屬性名 短屬性名 說 明

input $_ 最近一次要匹配的字符串。Opera未實(shí)現(xiàn)此屬性

lastMatch $& 最近一次的匹配項(xiàng)。Opera未實(shí)現(xiàn)此屬性

lastParen $+ 最近一次匹配的捕獲組。Opera未實(shí)現(xiàn)此屬性

leftContext $` input字符串中l(wèi)astMatch之前的文本

multiline $* 布爾值,表示是否所有表達(dá)式都使用多行模式。IE和Opera未實(shí)現(xiàn)此屬性

rightContext $' Input字符串中l(wèi)astMatch之后的文本

使用這些屬性可以從 exec()或 test()執(zhí)行的操作中提取出更具體的信息。請(qǐng)看下面的例子。

var text = \"this has been a short summer\";

var pattern = /(.)hort/g;

/*

* 注意:Opera 不支持 input、lastMatch、lastParen 和 multiline 屬性

* Internet Explorer 不支持 multiline 屬性

*/

if (pattern.test(text)){

alert(RegExp.input); // this has been a short summer

alert(RegExp.leftContext); // this has been a

alert(RegExp.rightContext); // summer

alert(RegExp.lastMatch); // short

alert(RegExp.lastParen); // s

alert(RegExp.multiline); // false

}

RegExpConstructorPropertiesExample01.htm

以上代碼創(chuàng)建了一個(gè)模式,匹配任何一個(gè)字符后跟 hort,而且把第一個(gè)字符放在了一個(gè)捕獲組中。

RegExp 構(gòu)造函數(shù)的各個(gè)屬性返回了下列值:

? input 屬性返回了原始字符串;

? leftContext 屬性返回了單詞 short 之前的字符串,而 rightContext 屬性則返回了 short

之后的字符串;

? lastMatch 屬性返回最近一次與整個(gè)正則表達(dá)式匹配的字符串,即 short;

? lastParen 屬性返回最近一次匹配的捕獲組,即例子中的 s。

如前所述,例子使用的長(zhǎng)屬性名都可以用相應(yīng)的短屬性名來代替。只不過,由于這些短屬性名大都

不是有效的 ECMAScript 標(biāo)識(shí)符,因此必須通過方括號(hào)語法來訪問它們,如下所示。

var text = \"this has been a short summer\";

var pattern = /(.)hort/g;

/*

* 注意:Opera 不支持 input、lastMatch、lastParen 和 multiline 屬性

* Internet Explorer 不支持 multiline 屬性

*/

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第127頁

5.4 RegExp 類型 109

1

2

3

4

5

13

6

7

8

9

10

11

12

if (pattern.test(text)){

alert(RegExp.$_); // this has been a short summer

alert(RegExp[\"$`\"]); // this has been a

alert(RegExp[\"$'\"]); // summer

alert(RegExp[\"$&\"]); // short

alert(RegExp[\"$+\"]); // s

alert(RegExp[\"$*\"]); // false

}

RegExpConstructorPropertiesExample02.htm

除了上面介紹的幾個(gè)屬性之外,還有多達(dá) 9 個(gè)用于存儲(chǔ)捕獲組的構(gòu)造函數(shù)屬性。訪問這些屬性的語

法是 RegExp.$1、RegExp.$2…RegExp.$9,分別用于存儲(chǔ)第一、第二……第九個(gè)匹配的捕獲組。在

調(diào)用 exec()或 test()方法時(shí),這些屬性會(huì)被自動(dòng)填充。然后,我們就可以像下面這樣來使用它們。

var text = \"this has been a short summer\";

var pattern = /(..)or(.)/g;

if (pattern.test(text)){

alert(RegExp.$1); //sh

alert(RegExp.$2); //t

}

RegExpConstructorPropertiesExample03.htm

這里創(chuàng)建了一個(gè)包含兩個(gè)捕獲組的模式,并用該模式測(cè)試了一個(gè)字符串。即使 test()方法只返回

一個(gè)布爾值,但 RegExp 構(gòu)造函數(shù)的屬性$1 和$2 也會(huì)被匹配相應(yīng)捕獲組的字符串自動(dòng)填充。

5.4.4 模式的局限性

盡管 ECMAScript 中的正則表達(dá)式功能還是比較完備的,但仍然缺少某些語言(特別是 Perl)所支

持的高級(jí)正則表達(dá)式特性。下面列出了 ECMAScript 正則表達(dá)式不支持的特性(要了解更多相關(guān)信息,

請(qǐng)?jiān)L問 www.regular-expressions.info)。

? 匹配字符串開始和結(jié)尾的\\A 和\\Z 錨①

? 向后查找(lookbehind)②

? 并集和交集類

? 原子組(atomic grouping)

? Unicode 支持(單個(gè)字符除外,如\?)

? 命名的捕獲組③

? s(single,單行)和 x(free-spacing,無間隔)匹配模式

? 條件匹配

? 正則表達(dá)式注釋

即使存在這些限制,ECMAScript 正則表達(dá)式仍然是非常強(qiáng)大的,能夠幫我們完成絕大多數(shù)模式匹

配任務(wù)。

——————————

① 但支持以插入符號(hào)(^)和美元符號(hào)($)來匹配字符串的開始和結(jié)尾。

② 但完全支持向前查找(lookahead)。

③ 但支持編號(hào)的捕獲組。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第128頁

110 第 5 章 引用類型

5.5 Function 類型

說起來 ECMAScript 中什么最有意思,我想那莫過于函數(shù)了——而有意思的根源,則在于函數(shù)實(shí)際

上是對(duì)象。每個(gè)函數(shù)都是 Function 類型的實(shí)例,而且都與其他引用類型一樣具有屬性和方法。由于函

數(shù)是對(duì)象,因此函數(shù)名實(shí)際上也是一個(gè)指向函數(shù)對(duì)象的指針,不會(huì)與某個(gè)函數(shù)綁定。函數(shù)通常是使用函

數(shù)聲明語法定義的,如下面的例子所示。

function sum (num1, num2) {

return num1 + num2;

}

這與下面使用函數(shù)表達(dá)式定義函數(shù)的方式幾乎相差無幾。

var sum = function(num1, num2){

return num1 + num2;

};

以上代碼定義了變量 sum 并將其初始化為一個(gè)函數(shù)。有讀者可能會(huì)注意到,function 關(guān)鍵字后面

沒有函數(shù)名。這是因?yàn)樵谑褂煤瘮?shù)表達(dá)式定義函數(shù)時(shí),沒有必要使用函數(shù)名——通過變量 sum 即可以引

用函數(shù)。另外,還要注意函數(shù)末尾有一個(gè)分號(hào),就像聲明其他變量時(shí)一樣。

最后一種定義函數(shù)的方式是使用 Function 構(gòu)造函數(shù)。Function 構(gòu)造函數(shù)可以接收任意數(shù)量的參數(shù),

但最后一個(gè)參數(shù)始終都被看成是函數(shù)體,而前面的參數(shù)則枚舉出了新函數(shù)的參數(shù)。來看下面的例子:

var sum = new Function(\"num1\", \"num2\", \"return num1 + num2\"); // 不推薦

從技術(shù)角度講,這是一個(gè)函數(shù)表達(dá)式。但是,我們不推薦讀者使用這種方法定義函數(shù),因?yàn)檫@種語

法會(huì)導(dǎo)致解析兩次代碼(第一次是解析常規(guī) ECMAScript 代碼,第二次是解析傳入構(gòu)造函數(shù)中的字符串),

從而影響性能。不過,這種語法對(duì)于理解“函數(shù)是對(duì)象,函數(shù)名是指針”的概念倒是非常直觀的。

由于函數(shù)名僅僅是指向函數(shù)的指針,因此函數(shù)名與包含對(duì)象指針的其他變量沒有什么不同。換句話

說,一個(gè)函數(shù)可能會(huì)有多個(gè)名字,如下面的例子所示。

function sum(num1, num2){

return num1 + num2;

}

alert(sum(10,10)); //20

var anotherSum = sum;

alert(anotherSum(10,10)); //20

sum = null;

alert(anotherSum(10,10)); //20

FunctionTypeExample01.htm

以上代碼首先定義了一個(gè)名為 sum()的函數(shù),用于求兩個(gè)值的和。然后,又聲明了變量 anotherSum,

并將其設(shè)置為與 sum 相等(將 sum 的值賦給 anotherSum)。注意,使用不帶圓括號(hào)的函數(shù)名是訪問函

數(shù)指針,而非調(diào)用函數(shù)。此時(shí),anotherSum 和 sum 就都指向了同一個(gè)函數(shù),因此 anotherSum()也

可以被調(diào)用并返回結(jié)果。即使將 sum 設(shè)置為 null,讓它與函數(shù)“斷絕關(guān)系”,但仍然可以正常調(diào)用

anotherSum()。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第129頁

5.5 Function 類型 111

1

2

3

4

5

13

6

7

8

9

10

11

12

5.5.1 沒有重載(深入理解)

將函數(shù)名想象為指針,也有助于理解為什么 ECMAScript 中沒有函數(shù)重載的概念。以下是曾在第 3

章使用過的例子。

function addSomeNumber(num){

return num + 100;

}

function addSomeNumber(num) {

return num + 200;

}

var result = addSomeNumber(100); //300

顯然,這個(gè)例子中聲明了兩個(gè)同名函數(shù),而結(jié)果則是后面的函數(shù)覆蓋了前面的函數(shù)。以上代碼實(shí)際

上與下面的代碼沒有什么區(qū)別。

var addSomeNumber = function (num){

return num + 100;

};

addSomeNumber = function (num) {

return num + 200;

};

var result = addSomeNumber(100); //300

通過觀察重寫之后的代碼,很容易看清楚到底是怎么回事兒——在創(chuàng)建第二個(gè)函數(shù)時(shí),實(shí)際上覆蓋

了引用第一個(gè)函數(shù)的變量 addSomeNumber。

5.5.2 函數(shù)聲明與函數(shù)表達(dá)式

本節(jié)到目前為止,我們一直沒有對(duì)函數(shù)聲明和函數(shù)表達(dá)式加以區(qū)別。而實(shí)際上,解析器在向執(zhí)行環(huán)

境中加載數(shù)據(jù)時(shí),對(duì)函數(shù)聲明和函數(shù)表達(dá)式并非一視同仁。解析器會(huì)率先讀取函數(shù)聲明,并使其在執(zhí)行

任何代碼之前可用(可以訪問);至于函數(shù)表達(dá)式,則必須等到解析器執(zhí)行到它所在的代碼行,才會(huì)真

正被解釋執(zhí)行。請(qǐng)看下面的例子。

alert(sum(10,10));

function sum(num1, num2){

return num1 + num2;

}

FunctionDeclarationExample01.htm

以上代碼完全可以正常運(yùn)行。因?yàn)樵诖a開始執(zhí)行之前,解析器就已經(jīng)通過一個(gè)名為函數(shù)聲明提升

(function declaration hoisting)的過程,讀取并將函數(shù)聲明添加到執(zhí)行環(huán)境中。對(duì)代碼求值時(shí),JavaScript

引擎在第一遍會(huì)聲明函數(shù)并將它們放到源代碼樹的頂部。所以,即使聲明函數(shù)的代碼在調(diào)用它的代碼后

面,JavaScript 引擎也能把函數(shù)聲明提升到頂部。如果像下面例子所示的,把上面的函數(shù)聲明改為等價(jià)

的函數(shù)表達(dá)式,就會(huì)在執(zhí)行期間導(dǎo)致錯(cuò)誤。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第130頁

112 第 5 章 引用類型

alert(sum(10,10));

var sum = function(num1, num2){

return num1 + num2;

};

FunctionInitializationExample01.htm

以上代碼之所以會(huì)在運(yùn)行期間產(chǎn)生錯(cuò)誤,原因在于函數(shù)位于一個(gè)初始化語句中,而不是一個(gè)函數(shù)聲

明。換句話說,在執(zhí)行到函數(shù)所在的語句之前,變量 sum 中不會(huì)保存有對(duì)函數(shù)的引用;而且,由于第一

行代碼就會(huì)導(dǎo)致“unexpected identifier”(意外標(biāo)識(shí)符)錯(cuò)誤,實(shí)際上也不會(huì)執(zhí)行到下一行。

除了什么時(shí)候可以通過變量訪問函數(shù)這一點(diǎn)區(qū)別之外,函數(shù)聲明與函數(shù)表達(dá)式的語法其實(shí)是等價(jià)的。

也可以同時(shí)使用函數(shù)聲明和函數(shù)表達(dá)式,例如 var sum = function sum(){}。

不過,這種語法在 Safari 中會(huì)導(dǎo)致錯(cuò)誤。

5.5.3 作為值的函數(shù)

因?yàn)?ECMAScript 中的函數(shù)名本身就是變量,所以函數(shù)也可以作為值來使用。也就是說,不僅可以

像傳遞參數(shù)一樣把一個(gè)函數(shù)傳遞給另一個(gè)函數(shù),而且可以將一個(gè)函數(shù)作為另一個(gè)函數(shù)的結(jié)果返回。來看

一看下面的函數(shù)。

function callSomeFunction(someFunction, someArgument){

return someFunction(someArgument);

}

這個(gè)函數(shù)接受兩個(gè)參數(shù)。第一個(gè)參數(shù)應(yīng)該是一個(gè)函數(shù),第二個(gè)參數(shù)應(yīng)該是要傳遞給該函數(shù)的一個(gè)值。

然后,就可以像下面的例子一樣傳遞函數(shù)了。

function add10(num){

return num + 10;

}

var result1 = callSomeFunction(add10, 10);

alert(result1); //20

function getGreeting(name){

return \"Hello, \" + name;

}

var result2 = callSomeFunction(getGreeting, \"Nicholas\");

alert(result2); //\"Hello, Nicholas\"

FunctionAsAnArgumentExample01.htm

這里的 callSomeFunction()函數(shù)是通用的,即無論第一個(gè)參數(shù)中傳遞進(jìn)來的是什么函數(shù),它都

會(huì)返回執(zhí)行第一個(gè)參數(shù)后的結(jié)果。還記得吧,要訪問函數(shù)的指針而不執(zhí)行函數(shù)的話,必須去掉函數(shù)名后

面的那對(duì)圓括號(hào)。因此上面例子中傳遞給 callSomeFunction()的是 add10 和 getGreeting,而不

是執(zhí)行它們之后的結(jié)果。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第131頁

5.5 Function 類型 113

1

2

3

4

5

13

6

7

8

9

10

11

12

當(dāng)然,可以從一個(gè)函數(shù)中返回另一個(gè)函數(shù),而且這也是極為有用的一種技術(shù)。例如,假設(shè)有一個(gè)

對(duì)象數(shù)組,我們想要根據(jù)某個(gè)對(duì)象屬性對(duì)數(shù)組進(jìn)行排序。而傳遞給數(shù)組 sort()方法的比較函數(shù)要接收

兩個(gè)參數(shù),即要比較的值。可是,我們需要一種方式來指明按照哪個(gè)屬性來排序。要解決這個(gè)問題,

可以定義一個(gè)函數(shù),它接收一個(gè)屬性名,然后根據(jù)這個(gè)屬性名來創(chuàng)建一個(gè)比較函數(shù),下面就是這個(gè)函

數(shù)的定義。

function createComparisonFunction(propertyName) {

return function(object1, object2){

var value1 = object1[propertyName];

var value2 = object2[propertyName];

if (value1 < value2){

return -1;

} else if (value1 > value2){

return 1;

} else {

return 0;

}

};

}

FunctionReturningFunctionExample01.htm

這個(gè)函數(shù)定義看起來有點(diǎn)復(fù)雜,但實(shí)際上無非就是在一個(gè)函數(shù)中嵌套了另一個(gè)函數(shù),而且內(nèi)部函數(shù)

前面加了一個(gè) return 操作符。在內(nèi)部函數(shù)接收到 propertyName 參數(shù)后,它會(huì)使用方括號(hào)表示法來

取得給定屬性的值。取得了想要的屬性值之后,定義比較函數(shù)就非常簡(jiǎn)單了。上面這個(gè)函數(shù)可以像在下

面例子中這樣使用。

var data = [{name: \"Zachary\", age: 28}, {name: \"Nicholas\", age: 29}];

data.sort(createComparisonFunction(\"name\"));

alert(data[0].name); //Nicholas

data.sort(createComparisonFunction(\"age\"));

alert(data[0].name); //Zachary

這里,我們創(chuàng)建了一個(gè)包含兩個(gè)對(duì)象的數(shù)組 data。其中,每個(gè)對(duì)象都包含一個(gè) name 屬性和一個(gè)

age 屬性。在默認(rèn)情況下,sort()方法會(huì)調(diào)用每個(gè)對(duì)象的 toString()方法以確定它們的次序;但得

到的結(jié)果往往并不符合人類的思維習(xí)慣。因此,我們調(diào)用 createComparisonFunction(\"name\")方

法創(chuàng)建了一個(gè)比較函數(shù),以便按照每個(gè)對(duì)象的 name 屬性值進(jìn)行排序。而結(jié)果排在前面的第一項(xiàng)是 name

為\"Nicholas\",age 是 29 的對(duì)象。然后,我們又使用了 createComparisonFunction(\"age\")返回

的比較函數(shù),這次是按照對(duì)象的 age 屬性排序。得到的結(jié)果是 name 值為\"Zachary\",age 值是 28 的

對(duì)象排在了第一位。

5.5.4 函數(shù)內(nèi)部屬性

在函數(shù)內(nèi)部,有兩個(gè)特殊的對(duì)象:arguments 和 this。其中,arguments 在第 3 章曾經(jīng)介紹過,

它是一個(gè)類數(shù)組對(duì)象,包含著傳入函數(shù)中的所有參數(shù)。雖然 arguments 的主要用途是保存函數(shù)參數(shù),

但這個(gè)對(duì)象還有一個(gè)名叫 callee 的屬性,該屬性是一個(gè)指針,指向擁有這個(gè) arguments 對(duì)象的函數(shù)。

請(qǐng)看下面這個(gè)非常經(jīng)典的階乘函數(shù)。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第132頁

114 第 5 章 引用類型

function factorial(num){

if (num <=1) {

return 1;

} else {

return num * factorial(num-1)

}

}

定義階乘函數(shù)一般都要用到遞歸算法;如上面的代碼所示,在函數(shù)有名字,而且名字以后也不會(huì)變

的情況下,這樣定義沒有問題。但問題是這個(gè)函數(shù)的執(zhí)行與函數(shù)名 factorial 緊緊耦合在了一起。為

了消除這種緊密耦合的現(xiàn)象,可以像下面這樣使用 arguments.callee。

function factorial(num){

if (num <=1) {

return 1;

} else {

return num * arguments.callee(num-1)

}

}

FunctionTypeArgumentsExample01.htm

在這個(gè)重寫后的 factorial()函數(shù)的函數(shù)體內(nèi),沒有再引用函數(shù)名 factorial。這樣,無論引用

函數(shù)時(shí)使用的是什么名字,都可以保證正常完成遞歸調(diào)用。例如:

var trueFactorial = factorial;

factorial = function(){

return 0;

};

alert(trueFactorial(5)); //120

alert(factorial(5)); //0

在此,變量 trueFactorial 獲得了 factorial 的值,實(shí)際上是在另一個(gè)位置上保存了一個(gè)函數(shù)

的指針。然后,我們又將一個(gè)簡(jiǎn)單地返回 0 的函數(shù)賦值給 factorial 變量。如果像原來的 factorial()

那樣不使用 arguments.callee,調(diào)用 trueFactorial()就會(huì)返回 0??墒牵诮獬撕瘮?shù)體內(nèi)的代

碼與函數(shù)名的耦合狀態(tài)之后,trueFactorial()仍然能夠正常地計(jì)算階乘;至于 factorial(),它現(xiàn)

在只是一個(gè)返回 0 的函數(shù)。

函數(shù)內(nèi)部的另一個(gè)特殊對(duì)象是 this,其行為與 Java 和 C#中的 this 大致類似。換句話說,this

引用的是函數(shù)據(jù)以執(zhí)行的環(huán)境對(duì)象——或者也可以說是 this 值(當(dāng)在網(wǎng)頁的全局作用域中調(diào)用函數(shù)時(shí),

this 對(duì)象引用的就是 window)。來看下面的例子。

window.color = \"red\";

var o = { color: \"blue\" };

function sayColor(){

alert(this.color);

}

sayColor(); //\"red\"

o.sayColor = sayColor;

o.sayColor(); //\"blue\"

FunctionTypeThisExample01.htm

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第133頁

5.5 Function 類型 115

1

2

3

4

5

13

6

7

8

9

10

11

12

上面這個(gè)函數(shù) sayColor()是在全局作用域中定義的,它引用了 this 對(duì)象。由于在調(diào)用函數(shù)之前,

this 的值并不確定,因此 this 可能會(huì)在代碼執(zhí)行過程中引用不同的對(duì)象。當(dāng)在全局作用域中調(diào)用

sayColor()時(shí),this 引用的是全局對(duì)象 window;換句話說,對(duì) this.color 求值會(huì)轉(zhuǎn)換成對(duì)

window.color 求值,于是結(jié)果就返回了\"red\"。而當(dāng)把這個(gè)函數(shù)賦給對(duì)象 o 并調(diào)用 o.sayColor()

時(shí),this 引用的是對(duì)象 o,因此對(duì) this.color 求值會(huì)轉(zhuǎn)換成對(duì) o.color 求值,結(jié)果就返回了\"blue\"。

請(qǐng)讀者一定要牢記,函數(shù)的名字僅僅是一個(gè)包含指針的變量而已。因此,即使是

在不同的環(huán)境中執(zhí)行,全局的 sayColor()函數(shù)與 o.sayColor()指向的仍然是同一

個(gè)函數(shù)。

ECMAScript 5 也規(guī)范化了另一個(gè)函數(shù)對(duì)象的屬性:caller。除了 Opera 的早期版本不支持,其他

瀏覽器都支持這個(gè) ECMAScript 3 并沒有定義的屬性。這個(gè)屬性中保存著調(diào)用當(dāng)前函數(shù)的函數(shù)的引用,

如果是在全局作用域中調(diào)用當(dāng)前函數(shù),它的值為 null。例如:

function outer(){

inner();

}

function inner(){

alert(inner.caller);

}

outer();

FunctionTypeArgumentsCallerExample01.htm

以上代碼會(huì)導(dǎo)致警告框中顯示 outer()函數(shù)的源代碼。因?yàn)?outer()調(diào)用了 inter(),所以

inner.caller 就指向 outer()。為了實(shí)現(xiàn)更松散的耦合,也可以通過 arguments.callee.caller

來訪問相同的信息。

function outer(){

inner();

}

function inner(){

alert(arguments.callee.caller);

}

outer();

FunctionTypeArgumentsCallerExample02.htm

IE、Firefox、Chrome 和 Safari 的所有版本以及 Opera 9.6 都支持 caller 屬性。

當(dāng)函數(shù)在嚴(yán)格模式下運(yùn)行時(shí),訪問 arguments.callee 會(huì)導(dǎo)致錯(cuò)誤。ECMAScript 5 還定義了

arguments.caller 屬性,但在嚴(yán)格模式下訪問它也會(huì)導(dǎo)致錯(cuò)誤,而在非嚴(yán)格模式下這個(gè)屬性始終是

undefined。定義這個(gè)屬性是為了分清 arguments.caller 和函數(shù)的 caller 屬性。以上變化都是為

了加強(qiáng)這門語言的安全性,這樣第三方代碼就不能在相同的環(huán)境里窺視其他代碼了。

嚴(yán)格模式還有一個(gè)限制:不能為函數(shù)的 caller 屬性賦值,否則會(huì)導(dǎo)致錯(cuò)誤。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第134頁

116 第 5 章 引用類型

5.5.5 函數(shù)屬性和方法

前面曾經(jīng)提到過,ECMAScript 中的函數(shù)是對(duì)象,因此函數(shù)也有屬性和方法。每個(gè)函數(shù)都包含兩個(gè)

屬性:length 和 prototype。其中,length 屬性表示函數(shù)希望接收的命名參數(shù)的個(gè)數(shù),如下面的例

子所示。

function sayName(name){

alert(name);

}

function sum(num1, num2){

return num1 + num2;

}

function sayHi(){

alert(\"hi\");

}

alert(sayName.length); //1

alert(sum.length); //2

alert(sayHi.length); //0

FunctionTypeLengthPropertyExample01.htm

以上代碼定義了 3 個(gè)函數(shù),但每個(gè)函數(shù)接收的命名參數(shù)個(gè)數(shù)不同。首先,sayName()函數(shù)定義了一

個(gè)參數(shù),因此其 length 屬性的值為 1。類似地,sum()函數(shù)定義了兩個(gè)參數(shù),結(jié)果其 length 屬性中

保存的值為 2。而 sayHi()沒有命名參數(shù),所以其 length 值為 0。

在 ECMAScript 核心所定義的全部屬性中,最耐人尋味的就要數(shù) prototype 屬性了。對(duì)于

ECMAScript 中的引用類型而言,prototype 是保存它們所有實(shí)例方法的真正所在。換句話說,諸如

toString()和 valueOf()等方法實(shí)際上都保存在 prototype 名下,只不過是通過各自對(duì)象的實(shí)例訪

問罷了。在創(chuàng)建自定義引用類型以及實(shí)現(xiàn)繼承時(shí),prototype 屬性的作用是極為重要的(第 6 章將詳

細(xì)介紹)。在 ECMAScript 5 中,prototype 屬性是不可枚舉的,因此使用 for-in 無法發(fā)現(xiàn)。

每個(gè)函數(shù)都包含兩個(gè)非繼承而來的方法:apply()和 call()。這兩個(gè)方法的用途都是在特定的作

用域中調(diào)用函數(shù),實(shí)際上等于設(shè)置函數(shù)體內(nèi) this 對(duì)象的值。首先,apply()方法接收兩個(gè)參數(shù):一個(gè)

是在其中運(yùn)行函數(shù)的作用域,另一個(gè)是參數(shù)數(shù)組。其中,第二個(gè)參數(shù)可以是 Array 的實(shí)例,也可以是

arguments 對(duì)象。例如:

function sum(num1, num2){

return num1 + num2;

}

function callSum1(num1, num2){

return sum.apply(this, arguments); // 傳入 arguments 對(duì)象

}

function callSum2(num1, num2){

return sum.apply(this, [num1, num2]); // 傳入數(shù)組

}

alert(callSum1(10,10)); //20

alert(callSum2(10,10)); //20

FunctionTypeApplyMethodExample01.htm

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第135頁

5.5 Function 類型 117

1

2

3

4

5

13

6

7

8

9

10

11

12

在上面這個(gè)例子中,callSum1()在執(zhí)行 sum()函數(shù)時(shí)傳入了 this 作為 this 值(因?yàn)槭窃谌?/p>

作用域中調(diào)用的,所以傳入的就是 window 對(duì)象)和 arguments 對(duì)象。而 callSum2 同樣也調(diào)用了

sum()函數(shù),但它傳入的則是 this 和一個(gè)參數(shù)數(shù)組。這兩個(gè)函數(shù)都會(huì)正常執(zhí)行并返回正確的結(jié)果。

在嚴(yán)格模式下,未指定環(huán)境對(duì)象而調(diào)用函數(shù),則 this 值不會(huì)轉(zhuǎn)型為 window。

除非明確把函數(shù)添加到某個(gè)對(duì)象或者調(diào)用 apply()或 call(),否則 this 值將是

undefined。

call()方法與 apply()方法的作用相同,它們的區(qū)別僅在于接收參數(shù)的方式不同。對(duì)于 call()

方法而言,第一個(gè)參數(shù)是 this 值沒有變化,變化的是其余參數(shù)都直接傳遞給函數(shù)。換句話說,在使用

call()方法時(shí),傳遞給函數(shù)的參數(shù)必須逐個(gè)列舉出來,如下面的例子所示。

function sum(num1, num2){

return num1 + num2;

}

function callSum(num1, num2){

return sum.call(this, num1, num2);

}

alert(callSum(10,10)); //20

FunctionTypeCallMethodExample01.htm

在使用 call()方法的情況下,callSum()必須明確地傳入每一個(gè)參數(shù)。結(jié)果與使用 apply()沒有

什么不同。至于是使用 apply()還是 call(),完全取決于你采取哪種給函數(shù)傳遞參數(shù)的方式最方便。

如果你打算直接傳入 arguments 對(duì)象,或者包含函數(shù)中先接收到的也是一個(gè)數(shù)組,那么使用 apply()

肯定更方便;否則,選擇 call()可能更合適。(在不給函數(shù)傳遞參數(shù)的情況下,使用哪個(gè)方法都無所

謂。)

事實(shí)上,傳遞參數(shù)并非 apply()和 call()真正的用武之地;它們真正強(qiáng)大的地方是能夠擴(kuò)充函數(shù)

賴以運(yùn)行的作用域。下面來看一個(gè)例子。

window.color = \"red\";

var o = { color: \"blue\" };

function sayColor(){

alert(this.color);

}

sayColor(); //red

sayColor.call(this); //red

sayColor.call(window); //red

sayColor.call(o); //blue

FunctionTypeCallExample01.htm

這個(gè)例子是在前面說明 this 對(duì)象的示例基礎(chǔ)上修改而成的。這一次,sayColor()也是作為全局

函數(shù)定義的,而且當(dāng)在全局作用域中調(diào)用它時(shí),它確實(shí)會(huì)顯示\"red\"——因?yàn)閷?duì) this.color 的求值會(huì)

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第136頁

118 第 5 章 引用類型

轉(zhuǎn)換成對(duì) window.color 的求值。而 sayColor.call(this)和 sayColor.call(window),則是兩

種顯式地在全局作用域中調(diào)用函數(shù)的方式,結(jié)果當(dāng)然都會(huì)顯示\"red\"。但是,當(dāng)運(yùn)行 sayColor.call(o)

時(shí),函數(shù)的執(zhí)行環(huán)境就不一樣了,因?yàn)榇藭r(shí)函數(shù)體內(nèi)的 this 對(duì)象指向了 o,于是結(jié)果顯示的是\"blue\"。

使用 call()(或 apply())來擴(kuò)充作用域的最大好處,就是對(duì)象不需要與方法有任何耦合關(guān)系。

在前面例子的第一個(gè)版本中,我們是先將 sayColor()函數(shù)放到了對(duì)象 o 中,然后再通過 o 來調(diào)用它的;

而在這里重寫的例子中,就不需要先前那個(gè)多余的步驟了。

ECMAScript 5 還定義了一個(gè)方法:bind()。這個(gè)方法會(huì)創(chuàng)建一個(gè)函數(shù)的實(shí)例,其 this 值會(huì)被綁

定到傳給 bind()函數(shù)的值。例如:

window.color = \"red\";

var o = { color: \"blue\" };

function sayColor(){

alert(this.color);

}

var objectSayColor = sayColor.bind(o);

objectSayColor(); //blue

FunctionTypeBindMethodExample01.htm

在這里,sayColor()調(diào)用 bind()并傳入對(duì)象 o,創(chuàng)建了 objectSayColor()函數(shù)。objectSayColor()函數(shù)的 this 值等于 o,因此即使是在全局作用域中調(diào)用這個(gè)函數(shù),也會(huì)看到\"blue\"。這

種技巧的優(yōu)點(diǎn)請(qǐng)參考第 22 章。

支持 bind()方法的瀏覽器有 IE9+、Firefox 4+、Safari 5.1+、Opera 12+和 Chrome。

每個(gè)函數(shù)繼承的 toLocaleString()和 toString()方法始終都返回函數(shù)的代碼。返回代碼的格

式則因?yàn)g覽器而異——有的返回的代碼與源代碼中的函數(shù)代碼一樣,而有的則返回函數(shù)代碼的內(nèi)部表

示,即由解析器刪除了注釋并對(duì)某些代碼作了改動(dòng)后的代碼。由于存在這些差異,我們無法根據(jù)這兩個(gè)

方法返回的結(jié)果來實(shí)現(xiàn)任何重要功能;不過,這些信息在調(diào)試代碼時(shí)倒是很有用。另外一個(gè)繼承的

valueOf()方法同樣也只返回函數(shù)代碼。

5.6 基本包裝類型

為了便于操作基本類型值,ECMAScript 還提供了 3 個(gè)特殊的引用類型:Boolean、Number 和

String。這些類型與本章介紹的其他引用類型相似,但同時(shí)也具有與各自的基本類型相應(yīng)的特殊行為。

實(shí)際上,每當(dāng)讀取一個(gè)基本類型值的時(shí)候,后臺(tái)就會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的基本包裝類型的對(duì)象,從而讓我們

能夠調(diào)用一些方法來操作這些數(shù)據(jù)。來看下面的例子。

var s1 = \"some text\";

var s2 = s1.substring(2);

這個(gè)例子中的變量 s1 包含一個(gè)字符串,字符串當(dāng)然是基本類型值。而下一行調(diào)用了 s1 的

substring()方法,并將返回的結(jié)果保存在了 s2 中。我們知道,基本類型值不是對(duì)象,因而從邏輯上

講它們不應(yīng)該有方法(盡管如我們所愿,它們確實(shí)有方法)。其實(shí),為了讓我們實(shí)現(xiàn)這種直觀的操作,

后臺(tái)已經(jīng)自動(dòng)完成了一系列的處理。當(dāng)?shù)诙写a訪問 s1 時(shí),訪問過程處于一種讀取模式,也就是要

從內(nèi)存中讀取這個(gè)字符串的值。而在讀取模式中訪問字符串時(shí),后臺(tái)都會(huì)自動(dòng)完成下列處理。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第137頁

5.6 基本包裝類型 119

1

2

3

4

5

13

6

7

8

9

10

11

12

(1) 創(chuàng)建 String 類型的一個(gè)實(shí)例;

(2) 在實(shí)例上調(diào)用指定的方法;

(3) 銷毀這個(gè)實(shí)例。

可以將以上三個(gè)步驟想象成是執(zhí)行了下列 ECMAScript 代碼。

var s1 = new String(\"some text\");

var s2 = s1.substring(2);

s1 = null;

經(jīng)過此番處理,基本的字符串值就變得跟對(duì)象一樣了。而且,上面這三個(gè)步驟也分別適用于 Boolean

和 Number 類型對(duì)應(yīng)的布爾值和數(shù)字值。

引用類型與基本包裝類型的主要區(qū)別就是對(duì)象的生存期。使用 new 操作符創(chuàng)建的引用類型的實(shí)例,

在執(zhí)行流離開當(dāng)前作用域之前都一直保存在內(nèi)存中。而自動(dòng)創(chuàng)建的基本包裝類型的對(duì)象,則只存在于一

行代碼的執(zhí)行瞬間,然后立即被銷毀。這意味著我們不能在運(yùn)行時(shí)為基本類型值添加屬性和方法。來看

下面的例子:

var s1 = \"some text\";

s1.color = \"red\";

alert(s1.color); //undefined

在此,第二行代碼試圖為字符串 s1 添加一個(gè) color 屬性。但是,當(dāng)?shù)谌写a再次訪問 s1 時(shí),

其 color 屬性不見了。問題的原因就是第二行創(chuàng)建的 String 對(duì)象在執(zhí)行第三行代碼時(shí)已經(jīng)被銷毀了。

第三行代碼又創(chuàng)建自己的 String 對(duì)象,而該對(duì)象沒有 color 屬性。

當(dāng)然,可以顯式地調(diào)用 Boolean、Number 和 String 來創(chuàng)建基本包裝類型的對(duì)象。不過,應(yīng)該在

絕對(duì)必要的情況下再這樣做,因?yàn)檫@種做法很容易讓人分不清自己是在處理基本類型還是引用類型的

值。對(duì)基本包裝類型的實(shí)例調(diào)用 typeof 會(huì)返回\"object\",而且所有基本包裝類型的對(duì)象都會(huì)被轉(zhuǎn)換

為布爾值 true。

Object 構(gòu)造函數(shù)也會(huì)像工廠方法一樣,根據(jù)傳入值的類型返回相應(yīng)基本包裝類型的實(shí)例。例如:

var obj = new Object(\"some text\");

alert(obj instanceof String); //true

把字符串傳給 Object 構(gòu)造函數(shù),就會(huì)創(chuàng)建 String 的實(shí)例;而傳入數(shù)值參數(shù)會(huì)得到 Number 的實(shí)

例,傳入布爾值參數(shù)就會(huì)得到 Boolean 的實(shí)例。

要注意的是,使用 new 調(diào)用基本包裝類型的構(gòu)造函數(shù),與直接調(diào)用同名的轉(zhuǎn)型函數(shù)是不一樣的。

例如:

var value = \"25\";

var number = Number(value); //轉(zhuǎn)型函數(shù)

alert(typeof number); //\"number\"

var obj = new Number(value); //構(gòu)造函數(shù)

alert(typeof obj); //\"object\"

在這個(gè)例子中,變量 number 中保存的是基本類型的值 25,而變量 obj 中保存的是 Number 的實(shí)

例。要了解有關(guān)轉(zhuǎn)型函數(shù)的更多信息,請(qǐng)參考第 3 章。

盡管我們不建議顯式地創(chuàng)建基本包裝類型的對(duì)象,但它們操作基本類型值的能力還是相當(dāng)重要的。

而每個(gè)基本包裝類型都提供了操作相應(yīng)值的便捷方法。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第138頁

120 第 5 章 引用類型

5.6.1 Boolean類型

Boolean 類型是與布爾值對(duì)應(yīng)的引用類型。要?jiǎng)?chuàng)建 Boolean 對(duì)象,可以像下面這樣調(diào)用 Boolean

構(gòu)造函數(shù)并傳入 true 或 false 值。

var booleanObject = new Boolean(true);

Boolean 類型的實(shí)例重寫了valueOf()方法,返回基本類型值true 或false;重寫了toString()

方法,返回字符串\"true\"和\"false\"??墒?,Boolean 對(duì)象在 ECMAScript 中的用處不大,因?yàn)樗?jīng)

常會(huì)造成人們的誤解。其中最常見的問題就是在布爾表達(dá)式中使用 Boolean 對(duì)象,例如:

var falseObject = new Boolean(false);

var result = falseObject && true;

alert(result); //true

var falseValue = false;

result = falseValue && true;

alert(result); //false

BooleanTypeExample01.htm

在這個(gè)例子中,我們使用 false 值創(chuàng)建了一個(gè) Boolean 對(duì)象。然后,將這個(gè)對(duì)象與基本類型值 true

構(gòu)成了邏輯與表達(dá)式。在布爾運(yùn)算中,false && true 等于 false??墒?,示例中的這行代碼是對(duì)

falseObject 而不是對(duì)它的值(false)進(jìn)行求值。前面討論過,布爾表達(dá)式中的所有對(duì)象都會(huì)被轉(zhuǎn)

換為 true,因此 falseObject 對(duì)象在布爾表達(dá)式中代表的是 true。結(jié)果,true && true 當(dāng)然就等

于 true 了。

基本類型與引用類型的布爾值還有兩個(gè)區(qū)別。首先,typeof 操作符對(duì)基本類型返回\"boolean\",

而對(duì)引用類型返回\"object\"。其次,由于 Boolean 對(duì)象是 Boolean 類型的實(shí)例,所以使用 instanceof

操作符測(cè)試 Boolean 對(duì)象會(huì)返回 true,而測(cè)試基本類型的布爾值則返回 false。例如:

alert(typeof falseObject); //object

alert(typeof falseValue); //boolean

alert(falseObject instanceof Boolean); //true

alert(falseValue instanceof Boolean); //false

理解基本類型的布爾值與 Boolean 對(duì)象之間的區(qū)別非常重要——當(dāng)然,我們的建議是永遠(yuǎn)不要使

用 Boolean 對(duì)象。

5.6.2 Number類型

Number 是與數(shù)字值對(duì)應(yīng)的引用類型。要?jiǎng)?chuàng)建 Number 對(duì)象,可以在調(diào)用 Number 構(gòu)造函數(shù)時(shí)向其

中傳遞相應(yīng)的數(shù)值。下面是一個(gè)例子。

var numberObject = new Number(10);

NumberTypeExample01.htm

與 Boolean 類型一樣,Number 類型也重寫了 valueOf()、toLocaleString()和 toString()

方法。重寫后的 valueOf()方法返回對(duì)象表示的基本類型的數(shù)值,另外兩個(gè)方法則返回字符串形式的

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第139頁

5.6 基本包裝類型 121

1

2

3

4

5

13

6

7

8

9

10

11

12

數(shù)值。我們?cè)诘?3 章還介紹過,可以為 toString()方法傳遞一個(gè)表示基數(shù)的參數(shù),告訴它返回幾進(jìn)制

數(shù)值的字符串形式,如下面的例子所示。

var num = 10;

alert(num.toString()); //\"10\"

alert(num.toString(2)); //\"1010\"

alert(num.toString(8)); //\"12\"

alert(num.toString(10)); //\"10\"

alert(num.toString(16)); //\"a\"

NumberTypeExample01.htm

除了繼承的方法之外,Number 類型還提供了一些用于將數(shù)值格式化為字符串的方法。

其中,toFixed()方法會(huì)按照指定的小數(shù)位返回?cái)?shù)值的字符串表示,例如:

var num = 10;

alert(num.toFixed(2)); //\"10.00\"

NumberTypeExample01.htm

這里給 toFixed()方法傳入了數(shù)值 2,意思是顯示幾位小數(shù)。于是,這個(gè)方法返回了\"10.00\",即

以 0 填補(bǔ)了必要的小數(shù)位。如果數(shù)值本身包含的小數(shù)位比指定的還多,那么接近指定的最大小數(shù)位的值

就會(huì)舍入,如下面的例子所示。

var num = 10.005;

alert(num.toFixed(2)); //\"10.01\"

能夠自動(dòng)舍入的特性,使得 toFixed()方法很適合處理貨幣值。但需要注意的是,不同瀏覽器給

這個(gè)方法設(shè)定的舍入規(guī)則可能會(huì)有所不同。在給 toFixed()傳入 0 的情況下,IE8 及之前版本不能正確

舍入范圍在{(?0.94,?0.5],[0.5,0.94)}之間的值。對(duì)于這個(gè)范圍內(nèi)的值,IE 會(huì)返回 0,而不是?1 或 1;其他

瀏覽器都能返回正確的值。IE9 修復(fù)了這個(gè)問題。

toFixed()方法可以表示帶有 0 到 20 個(gè)小數(shù)位的數(shù)值。但這只是標(biāo)準(zhǔn)實(shí)現(xiàn)的范

圍,有些瀏覽器也可能支持更多位數(shù)。

另外可用于格式化數(shù)值的方法是 toExponential(),該方法返回以指數(shù)表示法(也稱 e 表示法)

表示的數(shù)值的字符串形式。與 toFixed()一樣,toExponential()也接收一個(gè)參數(shù),而且該參數(shù)同樣

也是指定輸出結(jié)果中的小數(shù)位數(shù)??聪旅娴睦印?/p>

var num = 10;

alert(num.toExponential(1)); //\"1.0e+1\"

以上代碼輸出了\"1.0e+1\";不過,這么小的數(shù)值一般不必使用 e 表示法。如果你想得到表示某個(gè)

數(shù)值的最合適的格式,就應(yīng)該使用 toPrecision()方法。

對(duì)于一個(gè)數(shù)值來說,toPrecision()方法可能會(huì)返回固定大?。╢ixed)格式,也可能返回指數(shù)

(exponential)格式;具體規(guī)則是看哪種格式最合適。這個(gè)方法接收一個(gè)參數(shù),即表示數(shù)值的所有數(shù)字的

位數(shù)(不包括指數(shù)部分)。請(qǐng)看下面的例子。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第140頁

122 第 5 章 引用類型

var num = 99;

alert(num.toPrecision(1)); //\"1e+2\"

alert(num.toPrecision(2)); //\"99\"

alert(num.toPrecision(3)); //\"99.0\"

NumberTypeExample01.htm

以上代碼首先完成的任務(wù)是以一位數(shù)來表示 99,結(jié)果是\"1e+2\",即 100。因?yàn)橐晃粩?shù)無法準(zhǔn)確地

表示 99,因此 toPrecision()就將它向上舍入為 100,這樣就可以使用一位數(shù)來表示它了。而接下來的

用兩位數(shù)表示 99,當(dāng)然還是\"99\"。最后,在想以三位數(shù)表示 99 時(shí),toPrecision()方法返回了\"99.0\"。

實(shí)際上,toPrecision()會(huì)根據(jù)要處理的數(shù)值決定到底是調(diào)用 toFixed()還是調(diào)用 toExponential()。

而這三個(gè)方法都可以通過向上或向下舍入,做到以最準(zhǔn)確的形式來表示帶有正確小數(shù)位的值。

toPrecision()方法可以表現(xiàn) 1 到 21 位小數(shù)。某些瀏覽器支持的范圍更大,但

這是典型實(shí)現(xiàn)的范圍。

與 Boolean 對(duì)象類似,Number 對(duì)象也以后臺(tái)方式為數(shù)值提供了重要的功能。但與此同時(shí),我們?nèi)?/p>

然不建議直接實(shí)例化 Number 類型,而原因與顯式創(chuàng)建 Boolean 對(duì)象一樣。具體來講,就是在使用

typeof 和 instanceof 操作符測(cè)試基本類型數(shù)值與引用類型數(shù)值時(shí),得到的結(jié)果完全不同,如下面的

例子所示。

var numberObject = new Number(10);

var numberValue = 10;

alert(typeof numberObject); //\"object\"

alert(typeof numberValue); //\"number\"

alert(numberObject instanceof Number); //true

alert(numberValue instanceof Number); //false

在使用 typeof 操作符測(cè)試基本類型數(shù)值時(shí),始終會(huì)返回\"number\",而在測(cè)試 Number 對(duì)象時(shí),

則會(huì)返回\"object\"。類似地,Number 對(duì)象是 Number 類型的實(shí)例,而基本類型的數(shù)值則不是。

5.6.3 String類型

String 類型是字符串的對(duì)象包裝類型,可以像下面這樣使用 String 構(gòu)造函數(shù)來創(chuàng)建。

var stringObject = new String(\"hello world\");

StringTypeExample01.htm

String 對(duì)象的方法也可以在所有基本的字符串值中訪問到。其中,繼承的 valueOf()、toLocaleString()和 toString()方法,都返回對(duì)象所表示的基本字符串值。

String 類型的每個(gè)實(shí)例都有一個(gè) length 屬性,表示字符串中包含多個(gè)字符。來看下面的例子。

var stringValue = \"hello world\";

alert(stringValue.length); //\"11\"

這個(gè)例子輸出了字符串\"hello world\"中的字符數(shù)量,即\"11\"。應(yīng)該注意的是,即使字符串中包

含雙字節(jié)字符(不是占一個(gè)字節(jié)的 ASCII 字符),每個(gè)字符也仍然算一個(gè)字符。

String 類型提供了很多方法,用于輔助完成對(duì) ECMAScript 中字符串的解析和操作。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第141頁

5.6 基本包裝類型 123

1

2

3

4

5

13

6

7

8

9

10

11

12

1. 字符方法

兩個(gè)用于訪問字符串中特定字符的方法是:charAt()和 charCodeAt()。這兩個(gè)方法都接收一個(gè)

參數(shù),即基于 0 的字符位置。其中,charAt()方法以單字符字符串的形式返回給定位置的那個(gè)字符

(ECMAScript 中沒有字符類型)。例如:

var stringValue = \"hello world\";

alert(stringValue.charAt(1)); //\"e\"

字符串\"hello world\"位置 1 處的字符是\"e\",因此調(diào)用 charAt(1)就返回了\"e\"。如果你想得到

的不是字符而是字符編碼,那么就要像下面這樣使用 charCodeAt()了。

var stringValue = \"hello world\";

alert(stringValue.charCodeAt(1)); //輸出\"101\"

這個(gè)例子輸出的是\"101\",也就是小寫字母\"e\"的字符編碼。

ECMAScript 5 還定義了另一個(gè)訪問個(gè)別字符的方法。在支持此方法的瀏覽器中,可以使用方括號(hào)加數(shù)

字索引來訪問字符串中的特定字符,如下面的例子所示。

var stringValue = \"hello world\";

alert(stringValue[1]); //\"e\"

使用方括號(hào)表示法訪問個(gè)別字符的語法得到了 IE8 及 Firefox、Safari、Chrome 和 Opera 所有版本的

支持。如果是在 IE7 及更早版本中使用這種語法,會(huì)返回 undefined 值(盡管根本不是特殊的

undefined 值)。

2. 字符串操作方法

下面介紹與操作字符串有關(guān)的幾個(gè)方法。第一個(gè)就是 concat(),用于將一或多個(gè)字符串拼接起來,

返回拼接得到的新字符串。先來看一個(gè)例子。

var stringValue = \"hello \";

var result = stringValue.concat(\"world\");

alert(result); //\"hello world\"

alert(stringValue); //\"hello\"

在這個(gè)例子中,通過 stringValue 調(diào)用 concat()方法返回的結(jié)果是\"hello world\"——但

stringValue 的值則保持不變。實(shí)際上,concat()方法可以接受任意多個(gè)參數(shù),也就是說可以通過它

拼接任意多個(gè)字符串。再看一個(gè)例子:

var stringValue = \"hello \";

var result = stringValue.concat(\"world\", \"!\");

alert(result); //\"hello world!\"

alert(stringValue); //\"hello\"

這個(gè)例子將\"world\"和\"!\"拼接到了\"hello\"的末尾。雖然 concat()是專門用來拼接字符串的方

法,但實(shí)踐中使用更多的還是加號(hào)操作符(+)。而且,使用加號(hào)操作符在大多數(shù)情況下都比使用concat()

方法要簡(jiǎn)便易行(特別是在拼接多個(gè)字符串的情況下)。

ECMAScript還提供了三個(gè)基于子字符串創(chuàng)建新字符串的方法:slice()、substr()和 substring()。

這三個(gè)方法都會(huì)返回被操作字符串的一個(gè)子字符串,而且也都接受一或兩個(gè)參數(shù)。第一個(gè)參數(shù)指定子字

符串的開始位置,第二個(gè)參數(shù)(在指定的情況下)表示子字符串到哪里結(jié)束。具體來說,slice()和

substring()的第二個(gè)參數(shù)指定的是子字符串最后一個(gè)字符后面的位置。而 substr()的第二個(gè)參數(shù)指

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第142頁

124 第 5 章 引用類型

定的則是返回的字符個(gè)數(shù)。如果沒有給這些方法傳遞第二個(gè)參數(shù),則將字符串的長(zhǎng)度作為結(jié)束位置。與

concat()方法一樣,slice()、substr()和 substring()也不會(huì)修改字符串本身的值——它們只是

返回一個(gè)基本類型的字符串值,對(duì)原始字符串沒有任何影響。請(qǐng)看下面的例子。

var stringValue = \"hello world\";

alert(stringValue.slice(3)); //\"lo world\"

alert(stringValue.substring(3)); //\"lo world\"

alert(stringValue.substr(3)); //\"lo world\"

alert(stringValue.slice(3, 7)); //\"lo w\"

alert(stringValue.substring(3,7)); //\"lo w\"

alert(stringValue.substr(3, 7)); //\"lo worl\"

StringTypeManipulationMethodsExample01.htm

這個(gè)例子比較了以相同方式調(diào)用 slice()、substr()和 substring()得到的結(jié)果,而且多數(shù)情

況下的結(jié)果是相同的。在只指定一個(gè)參數(shù) 3 的情況下,這三個(gè)方法都返回\"lo world\",因?yàn)?#92;"hello\"

中的第二個(gè)\"l\"處于位置 3。而在指定兩個(gè)參數(shù) 3 和 7 的情況下,slice()和 substring()返回\"lo w\"

(\"world\"中的\"o\"處于位置 7,因此結(jié)果中不包含\"o\"),但 substr()返回\"lo worl\",因?yàn)樗牡诙?/p>

個(gè)參數(shù)指定的是要返回的字符個(gè)數(shù)。

在傳遞給這些方法的參數(shù)是負(fù)值的情況下,它們的行為就不盡相同了。其中,slice()方法會(huì)將傳

入的負(fù)值與字符串的長(zhǎng)度相加,substr()方法將負(fù)的第一個(gè)參數(shù)加上字符串的長(zhǎng)度,而將負(fù)的第二個(gè)

參數(shù)轉(zhuǎn)換為 0。最后,substring()方法會(huì)把所有負(fù)值參數(shù)都轉(zhuǎn)換為 0。下面來看例子。

var stringValue = \"hello world\";

alert(stringValue.slice(-3)); //\"rld\"

alert(stringValue.substring(-3)); //\"hello world\"

alert(stringValue.substr(-3)); //\"rld\"

alert(stringValue.slice(3, -4)); //\"lo w\"

alert(stringValue.substring(3, -4)); //\"hel\"

alert(stringValue.substr(3, -4)); //\"\"(空字符串)

StringTypeManipulationMethodsExample01.htm

這個(gè)例子清晰地展示了上述三個(gè)方法之間的不同行為。在給 slice()和 substr()傳遞一個(gè)負(fù)值

參數(shù)時(shí),它們的行為相同。這是因?yàn)?3 會(huì)被轉(zhuǎn)換為 8(字符串長(zhǎng)度加參數(shù) 11+(?3)=8),實(shí)際上相當(dāng)

于調(diào)用了 slice(8)和 substr(8)。但 substring()方法則返回了全部字符串,因?yàn)樗鼘?3 轉(zhuǎn)換

成了 0。

IE 的 JavaScript 實(shí)現(xiàn)在處理向 substr()方法傳遞負(fù)值的情況時(shí)存在問題,它會(huì)

返回原始的字符串。IE9 修復(fù)了這個(gè)問題。

當(dāng)?shù)诙€(gè)參數(shù)是負(fù)值時(shí),這三個(gè)方法的行為各不相同。slice()方法會(huì)把第二個(gè)參數(shù)轉(zhuǎn)換為 7,這

就相當(dāng)于調(diào)用了 slice(3,7),因此返回\"lo w\"。substring()方法會(huì)把第二個(gè)參數(shù)轉(zhuǎn)換為 0,使調(diào)

用變成了 substring(3,0),而由于這個(gè)方法會(huì)將較小的數(shù)作為開始位置,將較大的數(shù)作為結(jié)束位置,

因此最終相當(dāng)于調(diào)用了 substring(0,3)。substr()也會(huì)將第二個(gè)參數(shù)轉(zhuǎn)換為 0,這也就意味著返回

包含零個(gè)字符的字符串,也就是一個(gè)空字符串。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第143頁

5.6 基本包裝類型 125

1

2

3

4

5

13

6

7

8

9

10

11

12

3. 字符串位置方法

有兩個(gè)可以從字符串中查找子字符串的方法:indexOf()和 lastIndexOf()。這兩個(gè)方法都是從

一個(gè)字符串中搜索給定的子字符串,然后返子字符串的位置(如果沒有找到該子字符串,則返回-1)。

這兩個(gè)方法的區(qū)別在于:indexOf()方法從字符串的開頭向后搜索子字符串,而 lastIndexOf()方法

是從字符串的末尾向前搜索子字符串。還是來看一個(gè)例子吧。

var stringValue = \"hello world\";

alert(stringValue.indexOf(\"o\")); //4

alert(stringValue.lastIndexOf(\"o\")); //7

StringTypeLocationMethodsExample01.htm

子字符串\"o\"第一次出現(xiàn)的位置是 4,即\"hello\"中的\"o\";最后一次出現(xiàn)的位置是 7,即\"world\"中的

\"o\"。如果\"o\"在這個(gè)字符串中僅出現(xiàn)了一次,那么 indexOf()和 lastIndexOf()會(huì)返回相同的位置值。

這兩個(gè)方法都可以接收可選的第二個(gè)參數(shù),表示從字符串中的哪個(gè)位置開始搜索。換句話說,

indexOf()會(huì)從該參數(shù)指定的位置向后搜索,忽略該位置之前的所有字符;而 lastIndexOf()則會(huì)從

指定的位置向前搜索,忽略該位置之后的所有字符??聪旅娴睦印?/p>

var stringValue = \"hello world\";

alert(stringValue.indexOf(\"o\", 6)); //7

alert(stringValue.lastIndexOf(\"o\", 6)); //4

在將第二個(gè)參數(shù) 6 傳遞給這兩個(gè)方法之后,得到了與前面例子相反的結(jié)果。這一次,由于

indexOf()是從位置 6(字母\"w\")開始向后搜索,結(jié)果在位置 7 找到了\"o\",因此它返回 7。而 lastIndexOf()是從位置 6 開始向前搜索。結(jié)果找到了\"hello\"中的\"o\",因此它返回 4。在使用第二個(gè)

參數(shù)的情況下,可以通過循環(huán)調(diào)用 indexOf()或 lastIndexOf()來找到所有匹配的子字符串,如下

面的例子所示:

var stringValue = \"Lorem ipsum dolor sit amet, consectetur adipisicing elit\";

var positions = new Array();

var pos = stringValue.indexOf(\"e\");

while(pos > -1){

positions.push(pos);

pos = stringValue.indexOf(\"e\", pos + 1);

}

alert(positions); //\"3,24,32,35,52\"

StringTypeLocationMethodsExample02.htm

這個(gè)例子通過不斷增加 indexOf()方法開始查找的位置,遍歷了一個(gè)長(zhǎng)字符串。在循環(huán)之外,首

先找到了\"e\"在字符串中的初始位置;而進(jìn)入循環(huán)后,則每次都給 indexOf()傳遞上一次的位置加 1。

這樣,就確保了每次新搜索都從上一次找到的子字符串的后面開始。每次搜索返回的位置依次被保存在

數(shù)組 positions 中,以便將來使用。

4. trim()方法

ECMAScript 5 為所有字符串定義了 trim()方法。這個(gè)方法會(huì)創(chuàng)建一個(gè)字符串的副本,刪除前置及

后綴的所有空格,然后返回結(jié)果。例如:

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第144頁

126 第 5 章 引用類型

var stringValue = \" hello world \";

var trimmedStringValue = stringValue.trim();

alert(stringValue); //\" hello world \"

alert(trimmedStringValue); //\"hello world\"

由于 trim()返回的是字符串的副本,所以原始字符串中的前置及后綴空格會(huì)保持不變。支持這個(gè)

方法的瀏覽器有 IE9+、Firefox 3.5+、Safari 5+、Opera 10.5+和 Chrome。此外,F(xiàn)irefox 3.5+、Safari 5+

和 Chrome 8+還支持非標(biāo)準(zhǔn)的 trimLeft()和 trimRight()方法,分別用于刪除字符串開頭和末尾的

空格。

5. 字符串大小寫轉(zhuǎn)換方法

接下來我們要介紹的是一組與大小寫轉(zhuǎn)換有關(guān)的方法。ECMAScript 中涉及字符串大小寫轉(zhuǎn)換的方

法有 4 個(gè):toLowerCase()、toLocaleLowerCase()、toUpperCase()和 toLocaleUpperCase()。

其中,toLowerCase()和 toUpperCase()是兩個(gè)經(jīng)典的方法,借鑒自 java.lang.String 中的同名

方法。而 toLocaleLowerCase()和 toLocaleUpperCase()方法則是針對(duì)特定地區(qū)的實(shí)現(xiàn)。對(duì)有些地

區(qū)來說,針對(duì)地區(qū)的方法與其通用方法得到的結(jié)果相同,但少數(shù)語言(如土耳其語)會(huì)為 Unicode 大小

寫轉(zhuǎn)換應(yīng)用特殊的規(guī)則,這時(shí)候就必須使用針對(duì)地區(qū)的方法來保證實(shí)現(xiàn)正確的轉(zhuǎn)換。以下是幾個(gè)例子。

var stringValue = \"hello world\";

alert(stringValue.toLocaleUpperCase()); //\"HELLO WORLD\"

alert(stringValue.toUpperCase()); //\"HELLO WORLD\"

alert(stringValue.toLocaleLowerCase()); //\"hello world\"

alert(stringValue.toLowerCase()); //\"hello world\"

StringTypeCaseMethodExample01.htm

以上代碼調(diào)用的 toLocaleUpperCase()和 toUpperCase()都返回了\"HELLO WORLD\",就像調(diào)用

toLocaleLowerCase()和 toLowerCase()都返回\"hello world\"一樣。一般來說,在不知道自己的

代碼將在哪種語言環(huán)境中運(yùn)行的情況下,還是使用針對(duì)地區(qū)的方法更穩(wěn)妥一些。

6. 字符串的模式匹配方法

String 類型定義了幾個(gè)用于在字符串中匹配模式的方法。第一個(gè)方法就是 match(),在字符串上

調(diào)用這個(gè)方法,本質(zhì)上與調(diào)用 RegExp 的 exec()方法相同。match()方法只接受一個(gè)參數(shù),要么是一

個(gè)正則表達(dá)式,要么是一個(gè) RegExp 對(duì)象。來看下面的例子。

var text = \"cat, bat, sat, fat\";

var pattern = /.at/;

//與 pattern.exec(text)相同

var matches = text.match(pattern);

alert(matches.index); //0

alert(matches[0]); //\"cat\"

alert(pattern.lastIndex); //0

StringTypePatternMatchingExample01.htm

本例中的 match()方法返回了一個(gè)數(shù)組;如果是調(diào)用 RegExp 對(duì)象的 exec()方法并傳遞本例中的

字符串作為參數(shù),那么也會(huì)得到與此相同的數(shù)組:數(shù)組的第一項(xiàng)是與整個(gè)模式匹配的字符串,之后的每

一項(xiàng)(如果有)保存著與正則表達(dá)式中的捕獲組匹配的字符串。

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第145頁

5.6 基本包裝類型 127

1

2

3

4

5

13

6

7

8

9

10

11

12

另一個(gè)用于查找模式的方法是 search()。這個(gè)方法的唯一參數(shù)與 match()方法的參數(shù)相同:由字

符串或 RegExp 對(duì)象指定的一個(gè)正則表達(dá)式。search()方法返回字符串中第一個(gè)匹配項(xiàng)的索引;如果沒

有找到匹配項(xiàng),則返回-1。而且,search()方法始終是從字符串開頭向后查找模式??聪旅娴睦印?/p>

var text = \"cat, bat, sat, fat\";

var pos = text.search(/at/);

alert(pos); //1

StringTypePatternMatchingExample01.htm

這個(gè)例子中的 search()方法返回 1,即\"at\"在字符串中第一次出現(xiàn)的位置。

為了簡(jiǎn)化替換子字符串的操作,ECMAScript 提供了 replace()方法。這個(gè)方法接受兩個(gè)參數(shù):第

一個(gè)參數(shù)可以是一個(gè) RegExp 對(duì)象或者一個(gè)字符串(這個(gè)字符串不會(huì)被轉(zhuǎn)換成正則表達(dá)式),第二個(gè)參

數(shù)可以是一個(gè)字符串或者一個(gè)函數(shù)。如果第一個(gè)參數(shù)是字符串,那么只會(huì)替換第一個(gè)子字符串。要想替

換所有子字符串,唯一的辦法就是提供一個(gè)正則表達(dá)式,而且要指定全局(g)標(biāo)志,如下所示。

var text = \"cat, bat, sat, fat\";

var result = text.replace(\"at\", \"ond\");

alert(result); //\"cond, bat, sat, fat\"

result = text.replace(/at/g, \"ond\");

alert(result); //\"cond, bond, sond, fond\"

StringTypePatternMatchingExample01.htm

在這個(gè)例子中,首先傳入 replace()方法的是字符串\"at\"和替換用的字符串\"ond\"。替換的結(jié)果

是把\"cat\"變成了\"cond\",但字符串中的其他字符并沒有受到影響。然后,通過將第一個(gè)參數(shù)修改為帶

有全局標(biāo)志的正則表達(dá)式,就將全部\"at\"都替換成了\"ond\"。

如果第二個(gè)參數(shù)是字符串,那么還可以使用一些特殊的字符序列,將正則表達(dá)式操作得到的值插入

到結(jié)果字符串中。下表列出了 ECMAScript 提供的這些特殊的字符序列。

字符序列 替換文本

$$ $

$& 匹配整個(gè)模式的子字符串。與RegExp.lastMatch的值相同

$' 匹配的子字符串之前的子字符串。與RegExp.leftContext的值相同

$` 匹配的子字符串之后的子字符串。與RegExp.rightContext的值相同

$n 匹配第n個(gè)捕獲組的子字符串,其中n等于0~9。例如,$1是匹配第一個(gè)捕獲組的子字符串,$2是匹配第

二個(gè)捕獲組的子字符串,以此類推。如果正則表達(dá)式中沒有定義捕獲組,則使用空字符串

$nn 匹配第nn個(gè)捕獲組的子字符串,其中nn等于01~99。例如,$01是匹配第一個(gè)捕獲組的子字符串,$02

是匹配第二個(gè)捕獲組的子字符串,以此類推。如果正則表達(dá)式中沒有定義捕獲組,則使用空字符串

通過這些特殊的字符序列,可以使用最近一次匹配結(jié)果中的內(nèi)容,如下面的例子所示。

var text = \"cat, bat, sat, fat\";

result = text.replace(/(.at)/g, \"word ($1)\");

alert(result); //word (cat), word (bat), word (sat), word (fat)

StringTypePatternMatchingExample01.htm

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第146頁

128 第 5 章 引用類型

在此,每個(gè)以\"at\"結(jié)尾的單詞都被替換了,替換結(jié)果是\"word\"后跟一對(duì)圓括號(hào),而圓括號(hào)中是被

字符序列$1 所替換的單詞。

replace()方法的第二個(gè)參數(shù)也可以是一個(gè)函數(shù)。在只有一個(gè)匹配項(xiàng)(即與模式匹配的字符串)的

情況下,會(huì)向這個(gè)函數(shù)傳遞 3 個(gè)參數(shù):模式的匹配項(xiàng)、模式匹配項(xiàng)在字符串中的位置和原始字符串。在

正則表達(dá)式中定義了多個(gè)捕獲組的情況下,傳遞給函數(shù)的參數(shù)依次是模式的匹配項(xiàng)、第一個(gè)捕獲組的匹

配項(xiàng)、第二個(gè)捕獲組的匹配項(xiàng)……,但最后兩個(gè)參數(shù)仍然分別是模式的匹配項(xiàng)在字符串中的位置和原始

字符串。這個(gè)函數(shù)應(yīng)該返回一個(gè)字符串,表示應(yīng)該被替換的匹配項(xiàng)使用函數(shù)作為 replace()方法的第

二個(gè)參數(shù)可以實(shí)現(xiàn)更加精細(xì)的替換操作,請(qǐng)看下面這個(gè)例子。

function htmlEscape(text){

return text.replace(/[<>\"&]/g, function(match, pos, originalText){

switch(match){

case \"<\":

return \"&lt;\";

case \">\":

return \"&gt;\";

case \"&\":

return \"&amp;\";

case \"\\\"\":

return \"&quot;\";

}

});

}

alert(htmlEscape(\"<p class=\\\"greeting\\\">Hello world!</p>\"));

//&lt;p class=&quot;greeting&quot;&gt;Hello world!&lt;/p&gt;

StringTypePatternMatchingExample01.htm

這里,我們?yōu)椴迦?HTML 代碼定義了函數(shù) htmlEscape(),這個(gè)函數(shù)能夠轉(zhuǎn)義 4 個(gè)字符:小于號(hào)、

大于號(hào)、和號(hào)以及雙引號(hào)。實(shí)現(xiàn)這種轉(zhuǎn)義的最簡(jiǎn)單方式,就是使用正則表達(dá)式查找這幾個(gè)字符,然后定

義一個(gè)能夠針對(duì)每個(gè)匹配的字符返回特定 HTML 實(shí)體的函數(shù)。

最后一個(gè)與模式匹配有關(guān)的方法是 split(),這個(gè)方法可以基于指定的分隔符將一個(gè)字符串分割成

多個(gè)子字符串,并將結(jié)果放在一個(gè)數(shù)組中。分隔符可以是字符串,也可以是一個(gè) RegExp 對(duì)象(這個(gè)方

法不會(huì)將字符串看成正則表達(dá)式)。split()方法可以接受可選的第二個(gè)參數(shù),用于指定數(shù)組的大小,

以便確保返回的數(shù)組不會(huì)超過既定大小。請(qǐng)看下面的例子。

var colorText = \"red,blue,green,yellow\";

var colors1 = colorText.split(\",\"); //[\"red\", \"blue\", \"green\", \"yellow\"]

var colors2 = colorText.split(\",\", 2); //[\"red\", \"blue\"]

var colors3 = colorText.split(/[^\\,]+/); //[\"\", \",\", \",\", \",\", \"\"]

StringTypePatternMatchingExample01.htm

在這個(gè)例子中,colorText 是逗號(hào)分隔的顏色名字符串?;谠撟址{(diào)用 split(\",\")會(huì)得到

一個(gè)包含其中顏色名的數(shù)組,用于分割字符串的分隔符是逗號(hào)。為了將數(shù)組截短,讓它只包含兩項(xiàng),可

以為 split()方法傳遞第二個(gè)參數(shù) 2。最后,通過使用正則表達(dá)式,還可以取得包含逗號(hào)字符的數(shù)組。

需要注意的是,在最后一次調(diào)用 split()返回的數(shù)組中,第一項(xiàng)和最后一項(xiàng)是兩個(gè)空字符串。之所以會(huì)

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第147頁

5.6 基本包裝類型 129

1

2

3

4

5

13

6

7

8

9

10

11

12

這樣,是因?yàn)橥ㄟ^正則表達(dá)式指定的分隔符出現(xiàn)在了字符串的開頭(即子字符串\"red\")和末尾(即子

字符串\"yellow\")。

對(duì) split()中正則表達(dá)式的支持因?yàn)g覽器而異。盡管對(duì)于簡(jiǎn)單的模式?jīng)]有什么差別,但對(duì)于未發(fā)現(xiàn)

匹配項(xiàng)以及帶有捕獲組的模式,匹配的行為就不大相同了。以下是幾種常見的差別。

? IE8 及之前版本會(huì)忽略捕獲組。ECMA-262 規(guī)定應(yīng)該把捕獲組拼接到結(jié)果數(shù)組中。IE9 能正確地

在結(jié)果中包含捕獲組。

? Firefox 3.6 及之前版本在捕獲組未找到匹配項(xiàng)時(shí),會(huì)在結(jié)果數(shù)組中包含空字符串;ECMA-262 規(guī)

定沒有匹配項(xiàng)的捕獲組在結(jié)果數(shù)組中應(yīng)該用 undefined 表示。

在正則表達(dá)式中使用捕獲組時(shí)還有其他微妙的差別。在使用這種正則表達(dá)式時(shí),一定要在各種瀏覽

器下多做一些測(cè)試。

要了解關(guān)于 split()方法以及捕獲組的跨瀏覽器問題的更多討論,請(qǐng)參考 Steven

Levithan 的文章“JavaScript split bugs:Fixed!”(http://blog.stevenlevithan.com/archives/

cross-browser-split)。

7. localeCompare()方法

與操作字符串有關(guān)的最后一個(gè)方法是 localeCompare(),這個(gè)方法比較兩個(gè)字符串,并返回下列

值中的一個(gè):

? 如果字符串在字母表中應(yīng)該排在字符串參數(shù)之前,則返回一個(gè)負(fù)數(shù)(大多數(shù)情況下是-1,具體

的值要視實(shí)現(xiàn)而定);

? 如果字符串等于字符串參數(shù),則返回 0;

? 如果字符串在字母表中應(yīng)該排在字符串參數(shù)之后,則返回一個(gè)正數(shù)(大多數(shù)情況下是 1,具體的

值同樣要視實(shí)現(xiàn)而定)。

下面是幾個(gè)例子。

var stringValue = \"yellow\";

alert(stringValue.localeCompare(\"brick\")); //1

alert(stringValue.localeCompare(\"yellow\")); //0

alert(stringValue.localeCompare(\"zoo\")); //-1

StringTypeLocaleCompareExample01.htm

這個(gè)例子比較了字符串\"yellow\"和另外幾個(gè)值:\"brick\"、\"yellow\"和\"zoo\"。因?yàn)?#92;"brick\"在

字母表中排在\"yellow\"之前,所以 localeCompare()返回了 1;而\"yellow\"等于\"yellow\",所以

localeCompare()返回了 0;最后,\"zoo\"在字母表中排在\"yellow\"后面,所以 localeCompare()

返回了-1。再強(qiáng)調(diào)一次,因?yàn)?localeCompare()返回的數(shù)值取決于實(shí)現(xiàn),所以最好是像下面例子所示

的這樣使用這個(gè)方法。

function determineOrder(value) {

var result = stringValue.localeCompare(value);

if (result < 0){

alert(\"The string 'yellow' comes before the string '\" + value + \"'.\");

} else if (result > 0) {

alert(\"The string 'yellow' comes after the string '\" + value + \"'.\");

} else {

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第148頁

130 第 5 章 引用類型

alert(\"The string 'yellow' is equal to the string '\" + value + \"'.\");

}

}

determineOrder(\"brick\");

determineOrder(\"yellow\");

determineOrder(\"zoo\");

StringTypeLocaleCompareExample01.htm

使用這種結(jié)構(gòu),就可以確保自己的代碼在任何實(shí)現(xiàn)中都可以正確地運(yùn)行了。

localeCompare()方法比較與眾不同的地方,就是實(shí)現(xiàn)所支持的地區(qū)(國家和語言)決定了這個(gè)

方法的行為。比如,美國以英語作為 ECMAScript 實(shí)現(xiàn)的標(biāo)準(zhǔn)語言,因此 localeCompare()就是區(qū)分

大小寫的,于是大寫字母在字母表中排在小寫字母前頭就成為了一項(xiàng)決定性的比較規(guī)則。不過,在其他

地區(qū)恐怕就不是這種情況了。

8. fromCharCode()方法

另外,String 構(gòu)造函數(shù)本身還有一個(gè)靜態(tài)方法:fromCharCode()。這個(gè)方法的任務(wù)是接收一或

多個(gè)字符編碼,然后將它們轉(zhuǎn)換成一個(gè)字符串。從本質(zhì)上來看,這個(gè)方法與實(shí)例方法 charCodeAt()

執(zhí)行的是相反的操作。來看一個(gè)例子:

alert(String.fromCharCode(104, 101, 108, 108, 111)); //\"hello\"

StringTypeFromCharCodeExample01.htm

在這里,我們給 fromCharCode()傳遞的是字符串\"hello\"中每個(gè)字母的字符編碼。

9. HTML 方法

早期的 Web 瀏覽器提供商覺察到了使用 JavaScript 動(dòng)態(tài)格式化 HTML 的需求。于是,這些提供商就

擴(kuò)展了標(biāo)準(zhǔn),實(shí)現(xiàn)了一些專門用于簡(jiǎn)化常見 HTML 格式化任務(wù)的方法。下表列出了這些 HTML 方法。

不過,需要請(qǐng)讀者注意的是,應(yīng)該盡量不使用這些方法,因?yàn)樗鼈儎?chuàng)建的標(biāo)記通常無法表達(dá)語義。

方 法 輸出結(jié)果

anchor(name) <a name= \"name\">string</a>

big() <big>string</big>

bold() <b>string</b>

fixed() <tt>string</tt>

fontcolor(color) <font color=\"color\">string</font>

fontsize(size) <font size=\"size\">string</font>

italics() <i>string</i>

link(url) <a href=\"url\">string</a>

small() <small>string</small>

strike() <strike>string</strike>

sub() <sub>string</sub>

sup() <sup>string</sup>

5.7 單體內(nèi)置對(duì)象

ECMA-262 對(duì)內(nèi)置對(duì)象的定義是:“由 ECMAScript 實(shí)現(xiàn)提供的、不依賴于宿主環(huán)境的對(duì)象,這些對(duì)

象在 ECMAScript 程序執(zhí)行之前就已經(jīng)存在了?!币馑季褪钦f,開發(fā)人員不必顯式地實(shí)例化內(nèi)置對(duì)象,因

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第149頁

5.7 單體內(nèi)置對(duì)象 131

1

2

3

4

5

13

6

7

8

9

10

11

12

為它們已經(jīng)實(shí)例化了。前面我們已經(jīng)介紹了大多數(shù)內(nèi)置對(duì)象,例如 Object、Array 和 String。

ECMA-262 還定義了兩個(gè)單體內(nèi)置對(duì)象:Global 和 Math。

5.7.1 Global對(duì)象

Global(全局)對(duì)象可以說是 ECMAScript 中最特別的一個(gè)對(duì)象了,因?yàn)椴还苣銖氖裁唇嵌壬峡矗?/p>

這個(gè)對(duì)象都是不存在的。ECMAScript 中的 Global 對(duì)象在某種意義上是作為一個(gè)終極的“兜底兒對(duì)象”

來定義的。換句話說,不屬于任何其他對(duì)象的屬性和方法,最終都是它的屬性和方法。事實(shí)上,沒有全

局變量或全局函數(shù);所有在全局作用域中定義的屬性和函數(shù),都是 Global 對(duì)象的屬性。本書前面介紹

過的那些函數(shù),諸如 isNaN()、isFinite()、parseInt()以及 parseFloat(),實(shí)際上全都是 Global

對(duì)象的方法。除此之外,Global 對(duì)象還包含其他一些方法。

1. URI 編碼方法

Global 對(duì)象的 encodeURI()和 encodeURIComponent()方法可以對(duì) URI(Uniform Resource

Identifiers,通用資源標(biāo)識(shí)符)進(jìn)行編碼,以便發(fā)送給瀏覽器。有效的 URI 中不能包含某些字符,例如

空格。而這兩個(gè) URI 編碼方法就可以對(duì) URI 進(jìn)行編碼,它們用特殊的 UTF-8 編碼替換所有無效的字符,

從而讓瀏覽器能夠接受和理解。

其中,encodeURI()主要用于整個(gè) URI(例如,http://www.wrox.com/illegal value.htm),而 encodeURIComponent()主要用于對(duì) URI 中的某一段(例如前面 URI 中的 illegal value.htm)進(jìn)行編碼。

它們的主要區(qū)別在于,encodeURI()不會(huì)對(duì)本身屬于 URI 的特殊字符進(jìn)行編碼,例如冒號(hào)、正斜杠、

問號(hào)和井字號(hào);而 encodeURIComponent()則會(huì)對(duì)它發(fā)現(xiàn)的任何非標(biāo)準(zhǔn)字符進(jìn)行編碼。來看下面的例子。

var uri = \"http://www.wrox.com/illegal value.htm#start\";

//\"http://www.wrox.com/illegal%20value.htm#start\"

alert(encodeURI(uri));

//\"http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start\"

alert(encodeURIComponent(uri));

GlobalObjectURIEncodingExample01.htm

使用 encodeURI()編碼后的結(jié)果是除了空格之外的其他字符都原封不動(dòng),只有空格被替換成了

%20。而 encodeURIComponent()方法則會(huì)使用對(duì)應(yīng)的編碼替換所有非字母數(shù)字字符。這也正是可以

對(duì)整個(gè) URI 使用 encodeURI(),而只能對(duì)附加在現(xiàn)有 URI 后面的字符串使用 encodeURIComponent()

的原因所在。

一般來說,我們使用 encodeURIComponent() 方法的時(shí)候要比使用

encodeURI()更多,因?yàn)樵趯?shí)踐中更常見的是對(duì)查詢字符串參數(shù)而不是對(duì)基礎(chǔ) URI

進(jìn)行編碼。

與 encodeURI()和 encodeURIComponent()方法對(duì)應(yīng)的兩個(gè)方法分別是 decodeURI()和

decodeURIComponent()。其中,decodeURI()只能對(duì)使用 encodeURI()替換的字符進(jìn)行解碼。例如,

它可將%20 替換成一個(gè)空格,但不會(huì)對(duì)%23 作任何處理,因?yàn)?23 表示井字號(hào)(#),而井字號(hào)不是使用

encodeURI()替換的。同樣地,decodeURIComponent()能夠解碼使用 encodeURIComponent()編碼

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

第150頁

132 第 5 章 引用類型

的所有字符,即它可以解碼任何特殊字符的編碼。來看下面的例子:

var uri = \"http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start\";

//http%3A%2F%2Fwww.wrox.com%2Fillegal value.htm%23start

alert(decodeURI(uri));

//http://www.wrox.com/illegal value.htm#start

alert(decodeURIComponent(uri));

GlobalObjectURIDecodingExample01.htm

這里,變量 uri 包含著一個(gè)由 encodeURIComponent()編碼的字符串。在第一次調(diào)用 decodeURI()

輸出的結(jié)果中,只有%20 被替換成了空格。而在第二次調(diào)用 decodeURIComponent()輸出的結(jié)果中,

所有特殊字符的編碼都被替換成了原來的字符,得到了一個(gè)未經(jīng)轉(zhuǎn)義的字符串(但這個(gè)字符串并不是一

個(gè)有效的 URI)。

URI 方法 encodeURI()、encodeURIComponent()、decodeURI()和 decodeURIComponent()用于替代已經(jīng)被ECMA-262第3版廢棄的escape()和unescape()

方法。URI方法能夠編碼所有 Unicode字符,而原來的方法只能正確地編碼 ASCII 字符。

因此在開發(fā)實(shí)踐中,特別是在產(chǎn)品級(jí)的代碼中,一定要使用 URI方法,不要使用 escape()

和 unescape()方法。

2. eval()方法

現(xiàn)在,我們介紹最后一個(gè)——大概也是整個(gè) ECMAScript 語言中最強(qiáng)大的一個(gè)方法:eval()。eval()

方法就像是一個(gè)完整的 ECMAScript 解析器,它只接受一個(gè)參數(shù),即要執(zhí)行的 ECMAScript(或 JavaScript)

字符串??聪旅娴睦樱?/p>

eval(\"alert('hi')\");

這行代碼的作用等價(jià)于下面這行代碼:

alert(\"hi\");

當(dāng)解析器發(fā)現(xiàn)代碼中調(diào)用 eval()方法時(shí),它會(huì)將傳入的參數(shù)當(dāng)作實(shí)際的 ECMAScript 語句來解析,

然后把執(zhí)行結(jié)果插入到原位置。通過 eval()執(zhí)行的代碼被認(rèn)為是包含該次調(diào)用的執(zhí)行環(huán)境的一部分,

因此被執(zhí)行的代碼具有與該執(zhí)行環(huán)境相同的作用域鏈。這意味著通過 eval()執(zhí)行的代碼可以引用在包

含環(huán)境中定義的變量,舉個(gè)例子:

var msg = \"hello world\";

eval(\"alert(msg)\"); //\"hello world\"

可見,變量 msg 是在 eval()調(diào)用的環(huán)境之外定義的,但其中調(diào)用的 alert()仍然能夠顯示\"hello

world\"。這是因?yàn)樯厦娴诙写a最終被替換成了一行真正的代碼。同樣地,我們也可以在 eval()

調(diào)用中定義一個(gè)函數(shù),然后再在該調(diào)用的外部代碼中引用這個(gè)函數(shù):

eval(\"function sayHi() { alert('hi'); }\");

sayHi();

顯然,函數(shù) sayHi()是在 eval()內(nèi)部定義的。但由于對(duì) eval()的調(diào)用最終會(huì)被替換成定義函數(shù)

的實(shí)際代碼,因此可以在下一行調(diào)用 sayHi()。對(duì)于變量也一樣:

圖靈社區(qū)會(huì)員 StinkBC(StinkBC@gmail.com) 專享 尊重版權(quán)

百萬用戶使用云展網(wǎng)進(jìn)行書冊(cè)翻頁效果制作,只要您有文檔,即可一鍵上傳,自動(dòng)生成鏈接和二維碼(獨(dú)立電子書),支持分享到微信和網(wǎng)站!
收藏
轉(zhuǎn)發(fā)
下載
免費(fèi)制作
其他案例
更多案例
免費(fèi)制作
x
{{item.desc}}
下載
{{item.title}}
{{toast}}