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