ECMAScript 引用類(lèi)型
引用類(lèi)型通常叫做類(lèi)(class)。
本教程會(huì)討論大量的 ECMAScript 預(yù)定義引用類(lèi)型。
引用類(lèi)型
引用類(lèi)型通常叫做類(lèi)(class),也就是說(shuō),遇到引用值,所處理的就是對(duì)象。
本教程會(huì)討論大量的 ECMAScript 預(yù)定義引用類(lèi)型。
從現(xiàn)在起,將重點(diǎn)討論與已經(jīng)討論過(guò)的原始類(lèi)型緊密相關(guān)的引用類(lèi)型。
注意:從傳統(tǒng)意義上來(lái)說(shuō),ECMAScript 并不真正具有類(lèi)。事實(shí)上,除了說(shuō)明不存在類(lèi),在 ECMA-262 中根本沒(méi)有出現(xiàn)“類(lèi)”這個(gè)詞。ECMAScript 定義了“對(duì)象定義”,邏輯上等價(jià)于其他程序設(shè)計(jì)語(yǔ)言中的類(lèi)。
提示:本教程將使用術(shù)語(yǔ)“對(duì)象”。
對(duì)象是由 new 運(yùn)算符加上要實(shí)例化的對(duì)象的名字創(chuàng)建的。例如,下面的代碼創(chuàng)建 Object 對(duì)象的實(shí)例:
var o = new Object();
這種語(yǔ)法與 Java 語(yǔ)言的相似,不過(guò)當(dāng)有不止一個(gè)參數(shù)時(shí),ECMAScript 要求使用括號(hào)。如果沒(méi)有參數(shù),如以下代碼所示,括號(hào)可以省略:
var o = new Object;
注意:盡管括號(hào)不是必需的,但是為了避免混亂,最好使用括號(hào)。
提示:我們會(huì)在對(duì)象基礎(chǔ)這一章中更深入地探討對(duì)象及其行為。
這一節(jié)的重點(diǎn)是具有等價(jià)的原始類(lèi)型的引用類(lèi)型。
Object 對(duì)象
Object 對(duì)象自身用處不大,不過(guò)在了解其他類(lèi)之前,還是應(yīng)該了解它。因?yàn)?ECMAScript 中的 Object 對(duì)象與 Java 中的 java.lang.Object 相似,ECMAScript 中的所有對(duì)象都由這個(gè)對(duì)象繼承而來(lái),Object 對(duì)象中的所有屬性和方法都會(huì)出現(xiàn)在其他對(duì)象中,所以理解了 Object 對(duì)象,就可以更好地理解其他對(duì)象。
Object 對(duì)象具有下列屬性:
- constructor
- 對(duì)創(chuàng)建對(duì)象的函數(shù)的引用(指針)。對(duì)于 Object 對(duì)象,該指針指向原始的 Object() 函數(shù)。
- Prototype
- 對(duì)該對(duì)象的對(duì)象原型的引用。對(duì)于所有的對(duì)象,它默認(rèn)返回 Object 對(duì)象的一個(gè)實(shí)例。
Object 對(duì)象還具有幾個(gè)方法:
- hasOwnProperty(property)
- 判斷對(duì)象是否有某個(gè)特定的屬性。必須用字符串指定該屬性。(例如,o.hasOwnProperty("name"))
- IsPrototypeOf(object)
- 判斷該對(duì)象是否為另一個(gè)對(duì)象的原型。
- PropertyIsEnumerable
- 判斷給定的屬性是否可以用 for...in 語(yǔ)句進(jìn)行枚舉。
- ToString()
- 返回對(duì)象的原始字符串表示。對(duì)于 Object 對(duì)象,ECMA-262 沒(méi)有定義這個(gè)值,所以不同的 ECMAScript 實(shí)現(xiàn)具有不同的值。
- ValueOf()
- 返回最適合該對(duì)象的原始值。對(duì)于許多對(duì)象,該方法返回的值都與 ToString() 的返回值相同。
注釋?zhuān)?/span>上面列出的每種屬性和方法都會(huì)被其他對(duì)象覆蓋。
Boolean 對(duì)象
Boolean 對(duì)象是 Boolean 原始類(lèi)型的引用類(lèi)型。
要?jiǎng)?chuàng)建 Boolean 對(duì)象,只需要傳遞 Boolean 值作為參數(shù):
var oBooleanObject = new Boolean(true);
Boolean 對(duì)象將覆蓋 Object 對(duì)象的 ValueOf() 方法,返回原始值,即 true 和 false。ToString() 方法也會(huì)被覆蓋,返回字符串 "true" 或 "false"。
遺憾的是,在 ECMAScript 中很少使用 Boolean 對(duì)象,即使使用,也不易理解。
問(wèn)題通常出現(xiàn)在 Boolean 表達(dá)式中使用 Boolean 對(duì)象時(shí)。例如:
var oFalseObject = new Boolean(false);
var bResult = oFalseObject && true; //輸出 true
在這段代碼中,用 false 值創(chuàng)建 Boolean 對(duì)象。然后用這個(gè)值與原始值 true 進(jìn)行 AND 操作。在 Boolean 運(yùn)算中,false 和 true 進(jìn)行 AND 操作的結(jié)果是 false。不過(guò),在這行代碼中,計(jì)算的是 oFalseObject,而不是它的值 false。
正如前面討論過(guò)的,在 Boolean 表達(dá)式中,所有對(duì)象都會(huì)被自動(dòng)轉(zhuǎn)換為 true,所以 oFalseObject 的值是 true。然后 true 再與 true 進(jìn)行 AND 操作,結(jié)果為 true。
注意:雖然你應(yīng)該了解 Boolean 對(duì)象的可用性,不過(guò)最好還是使用 Boolean 原始值,避免發(fā)生這一節(jié)提到的問(wèn)題。
參閱
如需更多有關(guān) Boolean 對(duì)象的信息,請(qǐng)?jiān)L問(wèn) JavaScript Boolean 對(duì)象參考手冊(cè)。
Number 對(duì)象
正如你可能想到的,Number 對(duì)象是 Number 原始類(lèi)型的引用類(lèi)型。要?jiǎng)?chuàng)建 Number 對(duì)象,采用下列代碼:
var oNumberObject = new Number(68);
您應(yīng)該已認(rèn)出本章前面小節(jié)中討論特殊值(如 Number.MAX_VALUE)時(shí)提到的 Number 對(duì)象。所有特殊值都是 Number 對(duì)象的靜態(tài)屬性。
要得到數(shù)字對(duì)象的 Number 原始值,只需要使用 valueOf() 方法:
var iNumber = oNumberObject.valueOf();
當(dāng)然,Number 類(lèi)也有 toString() 方法,在討論類(lèi)型轉(zhuǎn)換的小節(jié)中已經(jīng)詳細(xì)討論過(guò)該方法。
除了從 Object 對(duì)象繼承的標(biāo)準(zhǔn)方法外,Number 對(duì)象還有幾個(gè)處理數(shù)值的專(zhuān)用方法。
toFixed() 方法
toFixed() 方法返回的是具有指定位數(shù)小數(shù)的數(shù)字的字符串表示。例如:
var oNumberObject = new Number(68);
alert(oNumberObject.toFixed(2)); //輸出 "68.00"
在這里,toFixed() 方法的參數(shù)是 2,說(shuō)明應(yīng)該顯示兩位小數(shù)。該方法返回 "68.00",空的字符串位由 0 來(lái)補(bǔ)充。對(duì)于處理貨幣的應(yīng)用程序,該方法非常有用。toFixed() 方法能表示具有 0 到 20 位小數(shù)的數(shù)字,超過(guò)這個(gè)范圍的值會(huì)引發(fā)錯(cuò)誤。
toExponential() 方法
與格式化數(shù)字相關(guān)的另一個(gè)方法是 toExponential(),它返回的是用科學(xué)計(jì)數(shù)法表示的數(shù)字的字符串形式。
與 toFixed() 方法相似,toExponential() 方法也有一個(gè)參數(shù),指定要輸出的小數(shù)的位數(shù)。例如:
var oNumberObject = new Number(68);
alert(oNumberObject.toExponential(1)); //輸出 "6.8e+1"
這段代碼的結(jié)果是 "6.8e+1",前面解釋過(guò),它表示 6.8x101。問(wèn)題是,如果不知道要用哪種形式(預(yù)定形式或指數(shù)形式)表示數(shù)字怎么辦?可以用 toPrecision() 方法。
toPrecision() 方法
toPrecision() 方法根據(jù)最有意義的形式來(lái)返回?cái)?shù)字的預(yù)定形式或指數(shù)形式。它有一個(gè)參數(shù),即用于表示數(shù)的數(shù)字總數(shù)(不包括指數(shù))。例如,
var oNumberObject = new Number(68);
alert(oNumberObject.toPrecision(1)); //輸出 "7e+1"
這段代碼的任務(wù)是用一位數(shù)字表示數(shù)字 68,結(jié)果為 "7e+1",以另外的形式表示即 70。的確,toPrecision() 方法會(huì)對(duì)數(shù)進(jìn)行舍入。不過(guò),如果用 2 位數(shù)字表示 68,就容易多了:
var oNumberObject = new Number(68);
alert(oNumberObject.toPrecision(2)); //輸出 "68"
當(dāng)然,輸出的是 "68",因?yàn)檫@正是該數(shù)的準(zhǔn)確表示。不過(guò),如果指定的位數(shù)多于需要的位數(shù)又如何呢?
var oNumberObject = new Number(68);
alert(oNumberObject.toPrecision(3)); //輸出 "68.0"
在這種情況下,toPrecision(3) 等價(jià)于 toFixed(1),輸出的是 "68.0"。
toFixed()、toExponential() 和 toPrecision() 方法都會(huì)進(jìn)行舍入操作,以便用正確的小數(shù)位數(shù)正確地表示一個(gè)數(shù)。
提示:與 Boolean 對(duì)象相似,Number 對(duì)象也很重要,不過(guò)應(yīng)該少用這種對(duì)象,以避免潛在的問(wèn)題。只要可能,都使用數(shù)字的原始表示法。
參閱
如需更多有關(guān) Number 對(duì)象的信息,請(qǐng)?jiān)L問(wèn) JavaScript Number 對(duì)象參考手冊(cè)。
String 對(duì)象
String 對(duì)象是 String 原始類(lèi)型的對(duì)象表示法,它是以下方式創(chuàng)建的:
var oStringObject = new String("hello world");
String 對(duì)象的 valueOf() 方法和 toString() 方法都會(huì)返回 String 類(lèi)型的原始值:
alert(oStringObject.valueOf() == oStringObject.toString()); //輸出 "true"
如果運(yùn)行這段代碼,輸出是 "true",說(shuō)明這些值真的相等。
注釋?zhuān)?/span>String 對(duì)象是 ECMAScript 中比較復(fù)雜的引用類(lèi)型之一。同樣,本節(jié)的重點(diǎn)只是 String 類(lèi)的基本功能。更多的高級(jí)功能請(qǐng)閱讀本教程相關(guān)的章節(jié),或參閱 JavaScript String 對(duì)象參考手冊(cè)。
length 屬性
String 對(duì)象具有屬性 length,它是字符串中的字符個(gè)數(shù):
var oStringObject = new String("hello world");
alert(oStringObject.length); //輸出 "11"
這個(gè)例子輸出的是 "11",即 "hello world" 中的字符個(gè)數(shù)。注意,即使字符串包含雙字節(jié)的字符(與 ASCII 字符相對(duì),ASCII 字符只占用一個(gè)字節(jié)),每個(gè)字符也只算一個(gè)字符。
charAt() 和 charCodeAt() 方法
String 對(duì)象還擁有大量的方法。
首先,兩個(gè)方法 charAt() 和 charCodeAt() 訪(fǎng)問(wèn)的是字符串中的單個(gè)字符。這兩個(gè)方法都有一個(gè)參數(shù),即要操作的字符的位置。
charAt() 方法返回的是包含指定位置處的字符的字符串:
var oStringObject = new String("hello world");
alert(oStringObject.charAt(1)); //輸出 "e"
在字符串 "hello world" 中,位置 1 處的字符是 "e"。在“ECMAScript 原始類(lèi)型”這一節(jié)中我們講過(guò),第一個(gè)字符的位置是 0,第二個(gè)字符的位置是 1,依此類(lèi)推。因此,調(diào)用 charAt(1) 返回的是 "e"。
如果想得到的不是字符,而是字符代碼,那么可以調(diào)用 charCodeAt() 方法:
var oStringObject = new String("hello world");
alert(oStringObject.charCodeAt(1)); //輸出 "101"
這個(gè)例子輸出 "101",即小寫(xiě)字母 "e" 的字符代碼。
concat() 方法
接下來(lái)是 concat() 方法,用于把一個(gè)或多個(gè)字符串連接到 String 對(duì)象的原始值上。該方法返回的是 String 原始值,保持原始的 String 對(duì)象不變:
var oStringObject = new String("hello "); var sResult = oStringObject.concat("world"); alert(sResult); //輸出 "hello world" alert(oStringObject); //輸出 "hello "
在上面這段代碼中,調(diào)用 concat() 方法返回的是 "hello world",而 String 對(duì)象存放的仍然是 "hello "。出于這種原因,較常見(jiàn)的是用加號(hào)(+)連接字符串,因?yàn)檫@種形式從邏輯上表明了真正的行為:
var oStringObject = new String("hello "); var sResult = oStringObject + "world"; alert(sResult); //輸出 "hello world" alert(oStringObject); //輸出 "hello "
indexOf() 和 lastIndexOf() 方法
迄今為止,已討論過(guò)連接字符串的方法,訪(fǎng)問(wèn)字符串中的單個(gè)字符的方法。不過(guò)如果無(wú)法確定在某個(gè)字符串中是否確實(shí)存在一個(gè)字符,應(yīng)該調(diào)用什么方法呢?這時(shí),可調(diào)用 indexOf() 和 lastIndexOf() 方法。
indexOf() 和 lastIndexOf() 方法返回的都是指定的子串在另一個(gè)字符串中的位置,如果沒(méi)有找不到子串,則返回 -1。
這兩個(gè)方法的不同之處在于,indexOf() 方法是從字符串的開(kāi)頭(位置 0)開(kāi)始檢索字符串,而 lastIndexOf() 方法則是從字符串的結(jié)尾開(kāi)始檢索子串。例如:
var oStringObject = new String("hello world!"); alert(oStringObject.indexOf("o")); 輸出 "4" alert(oStringObject.lastIndexOf("o")); 輸出 "7"
在這里,第一個(gè) "o" 字符串出現(xiàn)在位置 4,即 "hello" 中的 "o";最后一個(gè) "o" 出現(xiàn)在位置 7,即 "world" 中的 "o"。如果該字符串中只有一個(gè) "o" 字符串,那么 indexOf() 和 lastIndexOf() 方法返回的位置相同。
localeCompare() 方法
下一個(gè)方法是 localeCompare(),對(duì)字符串進(jìn)行排序。該方法有一個(gè)參數(shù) - 要進(jìn)行比較的字符串,返回的是下列三個(gè)值之一:
- 如果 String 對(duì)象按照字母順序排在參數(shù)中的字符串之前,返回負(fù)數(shù)。
- 如果 String 對(duì)象等于參數(shù)中的字符串,返回 0
- 如果 String 對(duì)象按照字母順序排在參數(shù)中的字符串之后,返回正數(shù)。
注釋?zhuān)?/span>如果返回負(fù)數(shù),那么最常見(jiàn)的是 -1,不過(guò)真正返回的是由實(shí)現(xiàn)決定的。如果返回正數(shù),那么同樣的,最常見(jiàn)的是 1,不過(guò)真正返回的是由實(shí)現(xiàn)決定的。
示例如下:
var oStringObject = new String("yellow"); alert(oStringObject.localeCompare("brick")); //輸出 "1" alert(oStringObject.localeCompare("yellow")); //輸出 "0" alert(oStringObject.localeCompare("zoo")); //輸出 "-1"
在這段代碼中,字符串 "yellow" 與 3 個(gè)值進(jìn)行了對(duì)比,即 "brick"、"yellow" 和 "zoo"。由于按照字母順序排列,"yellow" 位于 "brick" 之后,所以 localeCompare() 返回 1;"yellow" 等于 "yellow",所以 localeCompare() 返回 0;"zoo" 位于 "yellow" 之后,localeCompare() 返回 -1。再?gòu)?qiáng)調(diào)一次,由于返回的值是由實(shí)現(xiàn)決定的,所以最好以下面的方式調(diào)用 localeCompare() 方法:
var oStringObject1 = new String("yellow"); var oStringObject2 = new String("brick"); var iResult = oStringObject1.localeCompare(oStringObject2); if(iResult < 0) { alert(oStringObject1 + " comes before " + oStringObject2); } else if (iResult > 0) { alert(oStringObject1 + " comes after " + oStringObject2); } else { alert("The two strings are equal"); }
采用這種結(jié)構(gòu),可以確保這段代碼在所有實(shí)現(xiàn)中都能正確運(yùn)行。
localeCompare() 方法的獨(dú)特之處在于,實(shí)現(xiàn)所處的區(qū)域(locale,兼指國(guó)家/地區(qū)和語(yǔ)言)確切說(shuō)明了這種方法運(yùn)行的方式。在美國(guó),英語(yǔ)是 ECMAScript 實(shí)現(xiàn)的標(biāo)準(zhǔn)語(yǔ)言,localeCompare() 是區(qū)分大小寫(xiě)的,大寫(xiě)字母在字母順序上排在小寫(xiě)字母之后。不過(guò),在其他區(qū)域,情況可能并非如此。
slice() 和 substring()
ECMAScript 提供了兩種方法從子串創(chuàng)建字符串值,即 slice() 和 substring()。這兩種方法返回的都是要處理的字符串的子串,都接受一個(gè)或兩個(gè)參數(shù)。第一個(gè)參數(shù)是要獲取的子串的起始位置,第二個(gè)參數(shù)(如果使用的話(huà))是要獲取子串終止前的位置(也就是說(shuō),獲取終止位置處的字符不包括在返回的值內(nèi))。如果省略第二個(gè)參數(shù),終止位就默認(rèn)為字符串的長(zhǎng)度。
與 concat() 方法一樣,slice() 和 substring() 方法都不改變 String 對(duì)象自身的值。它們只返回原始的 String 值,保持 String 對(duì)象不變。
var oStringObject = new String("hello world"); alert(oStringObject.slice("3")); //輸出 "lo world" alert(oStringObject.substring("3")); //輸出 "lo world" alert(oStringObject.slice("3", "7")); //輸出 "lo w" alert(oStringObject.substring("3", "7")); //輸出 "lo w"
在這個(gè)例子中,slice() 和 substring() 的用法相同,返回值也一樣。當(dāng)只有參數(shù) 3 時(shí),兩個(gè)方法返回的都是 "lo world",因?yàn)?"hello" 中的第二個(gè) "l" 位于位置 3 上。當(dāng)有兩個(gè)參數(shù) "3" 和 "7" 時(shí),兩個(gè)方法返回的值都是 "lo w"("world" 中的字母 "o" 位于位置 7 上,所以它不包括在結(jié)果中)。
為什么有兩個(gè)功能完全相同的方法呢?事實(shí)上,這兩個(gè)方法并不完全相同,不過(guò)只在參數(shù)為負(fù)數(shù)時(shí),它們處理參數(shù)的方式才稍有不同。
對(duì)于負(fù)數(shù)參數(shù),slice() 方法會(huì)用字符串的長(zhǎng)度加上參數(shù),substring() 方法則將其作為 0 處理(也就是說(shuō)將忽略它)。例如:
var oStringObject = new String("hello world"); alert(oStringObject.slice("-3")); //輸出 "rld" alert(oStringObject.substring("-3")); //輸出 "hello world" alert(oStringObject.slice("3, -4")); //輸出 "lo w" alert(oStringObject.substring("3, -4")); //輸出 "hel"
這樣即可看出 slice() 和 substring() 方法的主要不同。
當(dāng)只有參數(shù) -3 時(shí),slice() 返回 "rld",substring() 則返回 "hello world"。這是因?yàn)閷?duì)于字符串 "hello world",slice("-3") 將被轉(zhuǎn)換成 slice("8"),而 substring("-3") 將被轉(zhuǎn)換成 substring("0")。
同樣,使用參數(shù) 3 和 -4 時(shí),差別也很明顯。slice() 將被轉(zhuǎn)換成 slice(3, 7),與前面的例子相同,返回 "lo w"。而 substring() 方法則將兩個(gè)參數(shù)解釋為 substring(3, 0),實(shí)際上即 substring(0, 3),因?yàn)?substring() 總把較小的數(shù)字作為起始位,較大的數(shù)字作為終止位。因此,substring("3, -4") 返回的是 "hel"。這里的最后一行代碼用來(lái)說(shuō)明如何使用這些方法。
toLowerCase()、toLocaleLowerCase()、toUpperCase() 和 toLocaleUpperCase()
最后一套要討論的方法涉及大小寫(xiě)轉(zhuǎn)換。有 4 種方法用于執(zhí)行大小寫(xiě)轉(zhuǎn)換,即
- toLowerCase()
- toLocaleLowerCase()
- toUpperCase()
- toLocaleUpperCase()
從名字上可以看出它們的用途,前兩種方法用于把字符串轉(zhuǎn)換成全小寫(xiě)的,后兩種方法用于把字符串轉(zhuǎn)換成全大寫(xiě)的。
toLowerCase() 和 toUpperCase() 方法是原始的,是以 java.lang.String 中相同方法為原型實(shí)現(xiàn)的。
toLocaleLowerCase() 和 toLocaleUpperCase() 方法是基于特定的區(qū)域?qū)崿F(xiàn)的(與 localeCompare() 方法相同)。在許多區(qū)域中,區(qū)域特定的方法都與通用的方法完全相同。不過(guò),有幾種語(yǔ)言對(duì) Unicode 大小寫(xiě)轉(zhuǎn)換應(yīng)用了特定的規(guī)則(例如土耳其語(yǔ)),因此必須使用區(qū)域特定的方法才能進(jìn)行正確的轉(zhuǎn)換。
var oStringObject = new String("Hello World"); alert(oStringObject.toLocaleUpperCase()); //輸出 "HELLO WORLD" alert(oStringObject.toUpperCase()); //輸出 "HELLO WORLD" alert(oStringObject.toLocaleLowerCase()); //輸出 "hello world" alert(oStringObject.toLowerCase()); //輸出 "hello world"
這段代碼中,toUpperCase() 和 toLocaleUpperCase() 輸出的都是 "HELLO WORLD",toLowerCase() 和 toLocaleLowerCase() 輸出的都是 "hello world"。一般來(lái)說(shuō),如果不知道在以哪種編碼運(yùn)行一種語(yǔ)言,則使用區(qū)域特定的方法比較安全。
提示:記住,String 對(duì)象的所有屬性和方法都可應(yīng)用于 String 原始值上,因?yàn)樗鼈兪莻螌?duì)象。
instanceof 運(yùn)算符
在使用 typeof 運(yùn)算符時(shí)采用引用類(lèi)型存儲(chǔ)值會(huì)出現(xiàn)一個(gè)問(wèn)題,無(wú)論引用的是什么類(lèi)型的對(duì)象,它都返回 "object"。ECMAScript 引入了另一個(gè) Java 運(yùn)算符 instanceof 來(lái)解決這個(gè)問(wèn)題。
instanceof 運(yùn)算符與 typeof 運(yùn)算符相似,用于識(shí)別正在處理的對(duì)象的類(lèi)型。與 typeof 方法不同的是,instanceof 方法要求開(kāi)發(fā)者明確地確認(rèn)對(duì)象為某特定類(lèi)型。例如:
var oStringObject = new String("hello world");
alert(oStringObject instanceof String); //輸出 "true"
這段代碼問(wèn)的是“變量 oStringObject 是否為 String 對(duì)象的實(shí)例?”oStringObject 的確是 String 對(duì)象的實(shí)例,因此結(jié)果是 "true"。盡管不像 typeof 方法那樣靈活,但是在 typeof 方法返回 "object" 的情況下,instanceof 方法還是很有用的。