這篇文章主要介紹了Javascript核心讀書有感之語句,需要的朋友可以參考下
在javascript中,表達(dá)式是短語,那么語句(statement)就是整句或命令。正如英文語句以句號結(jié)尾,javascript以分號結(jié)尾。
表達(dá)式計算出一個值,但語句使某件事發(fā)生。
“使某件事發(fā)生”的一個方法是計算帶有副作用的表達(dá)式。諸如賦值和函數(shù)調(diào)用這些有副作用的表達(dá)式,是可以作為單獨的語句的。這種把表達(dá)式當(dāng)做語句的用法也稱做表達(dá)式語句(expression statement)。類似的語句還有聲明語句(declaration statement),聲明語句用來聲明新變量或者定義新函數(shù)。
javascript程序就是一系列的可執(zhí)行語句的集合,默認(rèn)情況下,javascript解釋器依照編寫順序依次執(zhí)行。另一種“使某件事情”發(fā)生的方法就是改變語句的默認(rèn)執(zhí)行順序:
1.條件語句(conditional)語句:javascript解釋器可以根據(jù)一個表達(dá)式的值來判斷;來執(zhí)行還是跳過這些語句,例如if和switch語句。
2.循環(huán)語句(loop)語句:可以重復(fù)執(zhí)行的語句,例如while和for語句
3.跳轉(zhuǎn)(jump)語句:可以讓解釋器跳轉(zhuǎn)至程序的其它部分繼續(xù)執(zhí)行、例如break、return和throw語句
接下來本文將介紹javascript中各式各樣的語句和其語法。本章最后對這些語句做了總結(jié)。一個javascript程序無非是以分隔分割的語句集合,所以一旦掌握了javascript語句,就可以編寫javascript程序了。
1.表達(dá)式語句
賦值語句是一種比較重要的表達(dá)式語句,它的作用就是改變一個變量的值,就像執(zhí)行一條賦值語句一樣:例如
代碼如下:
greet = "hello" + name;
i *= 3;
遞增運算符(++)和遞減運算符(--)和賦值語句有關(guān)。它們的作用是改變一個變量的值,就像執(zhí)行一條賦值語句一樣。
代碼如下:
counter++;
delete運算符的重要作用就是刪除一個對象的屬性(或數(shù)組的元素),所有它一般作為語句使用,而不是作為復(fù)雜表達(dá)式的一部分。
代碼如下:
delete o.x;
函數(shù)調(diào)用是表達(dá)式語句的另外一個大類,例如
代碼如下:
alert(greet);
window.close();
雖然這些客戶端函數(shù)都是表達(dá)式,但它們對web瀏覽器造成了一定的影響。所以我們認(rèn)為也是語句,調(diào)用一個沒有副作用的函數(shù)是沒有意義的,除非它是復(fù)雜的表達(dá)式或賦值語句的一部分,例如。不可能隨便把一個余弦值丟棄;
Math.cos(x);
相反,得出余弦值就得把它賦值給一個變量,以便將來使用這個值:
var cx = Math.cos(x);
再次提醒各位,每行代碼就是以分號結(jié)束的。
2.復(fù)合語句和空語句
可以用逗號運算符將幾個表達(dá)式連接在一起,形成一個表達(dá)式。同樣,javascript還可以講多條語句聯(lián)合在一起,形成一個復(fù)合語句(compound statement)。只需花括號將多條語句括起來即可。因此,下面幾行代碼可以當(dāng)成一條單獨的語句,使用在javascript任何希望使用一條語句的地方。
代碼如下:
{
x = Math.PI;
cx = Math.cos(x);
console.log("cos(π)=" + cx);
}
關(guān)于語句塊有幾點需要注意:第一,語句塊不需要分號。塊中的元素語句必須以分號結(jié)尾,但語句塊不需要。
第二,語句塊中的行都有縮進(jìn),這不是必須的,但整齊的縮進(jìn)能使代碼可讀性更強(qiáng),更容易理解。
第三,javascript沒有塊級作用域,在語句塊中聲明的變量并不是語句塊所私有的。(參考3章10節(jié)第一小節(jié))
將很多條語句合并成一個大語句塊的做法在javascript編程中非常常見。類似的表達(dá)式通常包含子表達(dá)式一樣,很多javascript包含其它子語句,從形式來講,javascript通常允許一個語句塊包含一條子語句。例如:while循環(huán)的循環(huán)體就可以只包含一條語句。使用語句塊,可以將任意數(shù)量的語句放到這個塊中,這個語句塊可以當(dāng)做一條語句來使用。
在javascript中,當(dāng)希望多條語句被當(dāng)做一條語句使用時,使用符合語句來替代??照Z句(empty statement)則恰好相反,它允許包含0條語句??照Z句如下所示:
;//分號
javascript解釋器在執(zhí)行空語句時顯然不執(zhí)行任何動作,但實踐證明:當(dāng)創(chuàng)建一個具有空循環(huán)體的循環(huán)時,空語句有時是很有用的,例如下面的for循環(huán)
代碼如下:
//初始化一個數(shù)組a
for (i = 0; i < a.length; a[i++] = 0);
在這個循環(huán)中,所有的操作都在表達(dá)式a[i++]=0中完成,這里并不需要任何循環(huán)體。然而javascript需要循環(huán)體中至少包含一條語句,因此這里只使用了一個單獨的分號來表示一條空語句。
注意,在for循環(huán)、while循環(huán)或if語句的右邊園括號的分號很不起眼,這很可能造成 一些致命的bug,而這些bug很難定位到。例如下面的代碼的執(zhí)行結(jié)果很可能就是作者不想要的效果:
代碼如下:
if((a==0)||(b==0)); //這一行代碼什么也沒做....
o = null; //這一行代碼總會執(zhí)行
如果有特殊目的使用空語句,最好在代碼中添加注釋,這樣能更清楚的說明這條空語句是有用的
代碼如下:
for (i = 0; i < a.length; a[i++] = 0) /*empty*/;
3.聲明語句
var和function都是聲明語句,它們聲明或定義變量或函數(shù)。這些語句定義標(biāo)識符(變量名和函數(shù)名)并給其賦值,這些標(biāo)識符可以在程序任意地方使用。聲明語句本身什么也不做,但它有一個重要意義:通過創(chuàng)建變量和函數(shù),可以更好的組織代碼的語義。
接下幾節(jié)將講述var語句和function語句,但并不包含變量和函數(shù)的全部內(nèi)容。
i.var
var語句用來聲明一個或者多個變量,它的語法如下:
var name_1[ = value_1][, ..., name_n[ = value_n]]
關(guān)鍵字var之后跟隨的是要聲明的變量列表,列表中的每一個變量都可以帶有初始化表達(dá)式,可用于指定它的初始值。例如:
代碼如下:
var i; //一個簡單的變量
var j = 0; //一個帶有初始值的變量
var p, q; //兩個變量
var greet = "hello" + name; //更復(fù)雜的初始化表達(dá)式
var x = 2.34,y = Math.cos(0.75),r, theta; //很多變量
var x = 2,y = x * x; //第二個變量使用了第一個變量
var x = 2,
f = function(x) {return x * x}, //每個變量都獨占一行
y = f(x)
如果var語句出現(xiàn)在函數(shù)體內(nèi),那么定義的是一個局部變量,其作用域就是這個函數(shù)。如果在頂層代碼中使用var語句,那么它聲明的是全局變量,在整個javascript中,都是可見的。在第三章10節(jié)提到:全局變量是全局對象的屬性,然后和其它全局對象屬性不同的是,var聲明的變量是無法通過delete刪除的。
如果var語句中的變量沒有指定初始化表達(dá)式,那么這個變量的值初始為undefined。所以,在聲明語句之前的變量值就是undefined。
需要注意的是,var語句同樣可以作為for循環(huán)或者for/in循環(huán)的組成部分。(在循環(huán)之前聲明的變量聲明一樣,這里聲明變量也會"提前"),例如:
代碼如下:
for (var i = 0; i < 10; i++) console.log(i);
for (var i = 0, j = 10; i < 10; i++, j--) console.log(i * j);
for (var i in o)console.log(i);
注意,多次聲明同一變量是無所謂的。
ii.function
關(guān)鍵字function用來聲明函數(shù)的,我們已經(jīng)學(xué)過函數(shù)表達(dá)式(4.3).函數(shù)定義可以寫成語句的形式。例如:下面示例代碼中的兩種定義寫法:
代碼如下:
var f = function f(x) {return x + 1;} //將表達(dá)式賦值給一個變量
function f(x){return x + 1;} //含有變量名的語句
函數(shù)聲明的語法如下:
代碼如下:
function funcname([arg1[, arg2[..., argn]]]) {
statements
}
funcname是要聲明的函數(shù)的名稱標(biāo)識符。函數(shù)名之后是參數(shù)列表,參數(shù)之間使用逗號隔開。當(dāng)調(diào)用函數(shù)的時候,這些標(biāo)識符則指代傳入函數(shù)的實參。
函數(shù)體是由javascript語句組成的,語句數(shù)量不限,且用花括號括起來。在定義函數(shù)時,并不執(zhí)行函數(shù)體內(nèi)的語句,它和調(diào)用函數(shù)時待執(zhí)行的新函數(shù)對象相關(guān)聯(lián)。注意,function函數(shù)語句里的花括號是必須的,這和while循環(huán)和其它一些語句鎖使用的語句塊是不同的,即使函數(shù)體只有一條語句,仍然需要花括號將其括起來。
代碼如下:
function hyteus(x, y) {
return Math.sqrt(x * x + y * y);
}
hyteus(1, 2) //=>2.23606797749979
function facial(n) { //一個遞歸函數(shù)
if (n <= 1) return 1;
return n * facial(n - 1);
}
facial(11) //=>39916800
函數(shù)的聲明通常出現(xiàn)在javascript代碼的最頂部,也可以嵌套在其他函數(shù)體內(nèi)。但在嵌套時,函數(shù)聲明只能出現(xiàn)在所嵌套的函數(shù)頂部。也就是說:函數(shù)定義不能出現(xiàn)在if、while、或其他語句中。
和var語句一樣,函數(shù)聲明語句創(chuàng)建的變量也是不可刪除的。但是這些變量不是只讀的,變量值可以重寫。
4.條件語句
條件語句是通過判斷指定的表達(dá)式的值是否來執(zhí)行或跳過某些語句。這些語句是代碼的”決策點“,有時稱為”分支“。如果javascript解釋器是按照代碼的”路徑“執(zhí)行的。條件語句就是這條路上的分叉點。程序到達(dá)這里必須選擇一條路徑來繼續(xù)執(zhí)行。
i.if語句
if語句是基本的控制語句,準(zhǔn)確的說,它讓程序有條件的執(zhí)行,這種語句有兩種形式:第一種是
代碼如下:
if (expression)
statement
這種形式中,判斷expression 的值,如果是真,執(zhí)行statement語句,如果是假值,就不執(zhí)行statement.例如
代碼如下:
if (username == null) //如果username是null或undefined
username = "jack wong"; //對其進(jìn)行定義
需要注意的是,if語句括住expression的園括號是必須的。
javascript語法規(guī)定,if關(guān)鍵字和帶園括號的表達(dá)式之后必須跟隨一條語句。但可以使用語句塊將多條語句合成一條。因此,if語句的形式如下所示:
代碼如下:
if (!address) {
address = "";
message = "please mailing address"
}
if語句的第二種形式引入了else從句,當(dāng)expression的值是false值時執(zhí)行else 邏輯
代碼如下:
if (expression)
statement1
else
statement2
例如以下代碼
代碼如下:
if (n == 1)
console.log("1 new message");
else
console.log("you have" + n + "new message");
當(dāng)if/else語句中,嵌套使用if語句時,必須注意確保else語句匹配正確的if語句??紤]如下代碼:
代碼如下:
i = j = 1;
k = 2;
if (i == j)
if (j == k)
console.log("i equs k");
else
console.log("i dosent equal j"); //錯誤!!
這個實例中,內(nèi)層的if語句構(gòu)成了外層if語句所需要的子句。但是,if和else的匹配關(guān)系不清晰(只有縮進(jìn)給了一點暗示)而且在這個例子里,縮進(jìn)給出的暗示是錯誤的,因為javascript解釋器是這么理解的。
代碼如下:
if (i == j) {
if (j == k)
console.log("i equs k");
else
console.log("i dosent equal j");
}
和大多編程語言一樣,javascript中的if、else匹配規(guī)則是,else總是和就近的if語句匹配,為了讓個例子的可讀性更強(qiáng),更容易理解,更方便維護(hù)和調(diào)試,應(yīng)當(dāng)使用花括號
代碼如下:
if (i == j) {
if (j == k) {
console.log("i equs k");
} else { //花括號使代碼的結(jié)果更清晰
console.log("i dosent equal j");
}
}
許多程序員都將有if和else語句主體用花括號括起來的習(xí)慣(就像類似while循環(huán)這樣的符合語句中一樣),即使每條分支只有一條語句,但這樣做能避免剛才的程序歧義問題。
ii.else if
if/else語句通過判斷一個表達(dá)式的計算結(jié)果來選擇兩條分支中的一條。當(dāng)代碼中有許多條分支的時候應(yīng)該怎么辦呢?一種解決的辦法是使用else if語句。else if并不是真正的javascript語句,它只不過是多條if / else語句連接在一起的寫法。
代碼如下:
if (n == 1) {
//執(zhí)行代碼塊 1
} else if (n == 2) {
//執(zhí)行代碼塊2
} else if (n == 3) {
//執(zhí)行代碼塊3
} else {
//之前的條件都為false,則執(zhí)行代碼塊4
}
這種代碼沒有什么特別之處,它由多條if語句組成,每條if語句的else的從句又包含另外一條if語句。可以用if語句的嵌套形式來完成語法上的等價代碼,但與此相比,顯然else if的寫法更加清晰也更可取。
iii.switch
if語句在程序執(zhí)行的過程中,創(chuàng)建一支分支,并且可以使用else if來處理多條分支。然后,當(dāng)所有的分支都依賴同一個表達(dá)式的值時,else if并不是最佳的解決方案。在這種情況下,重復(fù)計算多條if語句中的表達(dá)式是非常浪費的做法。
switch語句適合處理這種情況。關(guān)鍵字switch之后緊跟著園括號括起來的一個表達(dá)式。隨后是花括號括起來的代碼塊。
代碼如下:
switch (expression) {
statements
}
然而switch語句完整的語法要比這更復(fù)雜一些。case之后是一個表達(dá)式和冒號,case和標(biāo)記語很類似,只是這個標(biāo)記語并沒有名字。
它只和他后面的表達(dá)式關(guān)聯(lián)在一起。當(dāng)執(zhí)行執(zhí)行這條switch語句時,它首先計算expression的值,然后查找case子句的表達(dá)式是否和expression的值相同。(這里的相同是按照“===”運算符進(jìn)行比較的),如果匹配case,它將會執(zhí)行對應(yīng)的代碼。如果找不到匹配的case,它將會執(zhí)行"default:"標(biāo)簽中的代碼塊。如果沒有“default:”標(biāo)簽,switch將跳過所有的代碼塊。
switch語句是非常容易混淆的,用例子介紹會比較清晰一點,下面的switch語句和方才的if/else語句是等價的
代碼如下:
switch (n) {
case 1: //如果n ===1從這里開始
//執(zhí)行代碼塊1
break;
case 2:
//執(zhí)行代碼塊2
break;
case 3:
//執(zhí)行代碼塊3
break;
default:
//執(zhí)行代碼塊4
break;
}
需要注意的是,每個case語句的結(jié)尾處都使用了關(guān)鍵字break。我們將后面介紹break語句,break語句可以使解釋器跳出switch語句或循環(huán)語句。在switch中,case只是指明了要執(zhí)行的代碼起點,但沒有指明終點。如果沒有break語句,那么switch語句就從expression的值的匹配的case標(biāo)簽處代碼開始執(zhí)行,依次執(zhí)行后續(xù)的語句,一直到整個switch代碼塊結(jié)束。當(dāng)然,如果在函數(shù)中使用switch語句,可以使用return來替換break,return和break都用于終止switch語句,也會防止一個case語句執(zhí)行完繼續(xù)執(zhí)行下一個case語句塊。
下面的語句貼近實戰(zhàn),它根據(jù)值的類型將該值轉(zhuǎn)換為字符串。
代碼如下:
function convert(x) {
switch (typeof x) {
case 'number': //將數(shù)字轉(zhuǎn)換為16進(jìn)制
return x.toString(16);
case 'string':
return '"' + x + '"'; //返回兩段帶雙引號的字符串。
default: //使用普通方法轉(zhuǎn)換其它類型
return String(x);
}
}
console.log(convert(100255114)) //=>5f9c58a
注意,在上面的兩個例子中,case關(guān)鍵字后跟隨的是數(shù)字和字符串直接量,在實際中這是switch最常見的用法,但是ECMAScript標(biāo)準(zhǔn)允許每個關(guān)鍵字跟隨任意的表達(dá)式。
switch語句首先計算switch 關(guān)鍵字后的表達(dá)式,然后按照從上到下的順序計算每個case后的表達(dá)式,知道執(zhí)行到case的表達(dá)式的值和switch的表達(dá)式的值相等時為止。由于對每個case的匹配操作實際上是“===”恒等運算符比較,而不是“==”,因此表達(dá)式和case的匹配并不會做任何類型轉(zhuǎn)換。
每次執(zhí)行switch語句的時候,并不是所有的case表達(dá)式都能執(zhí)行到,因此,應(yīng)當(dāng)避免帶有副作用的case表達(dá)式,比如函數(shù)調(diào)用的表達(dá)式和賦值表達(dá)式。最安全的做法就是在case表達(dá)式中使用常量表達(dá)式。
前面提到過,switch表達(dá)式與所有的case表達(dá)式都不匹配,則執(zhí)行標(biāo)記為“default:”的語句塊,如果沒有"default:"標(biāo)簽,則switch整個語句都跳過。在之前的例子中,“default:”標(biāo)簽都出現(xiàn)在switch末尾,位于所有case標(biāo)簽之后,當(dāng)然這是最合理也是最常用的寫法。實際上,“default:”標(biāo)簽可以放在switch語句內(nèi)任何地方。
5.循環(huán)。
為了理解條件語句,可以將javascript中的代碼想成一條條分支路徑。循環(huán)語句(looping statement)就是程序路徑的一個回路,可以讓一部分代碼重復(fù)執(zhí)行。javascript中有四種循環(huán)語句:while、do/while、for、for/in下面幾節(jié)會一次講解他們。其中最常用的循環(huán)就是數(shù)組元素的遍歷,(7.6會詳細(xì)討論這種循環(huán)和使用數(shù)組類定義的特殊循環(huán)方法。)
i.while
if語句是一種基本的控制語句,用來選擇執(zhí)行程序的分支語句。和if一樣,while語句也是一個基本的循環(huán)語句,它的語法如下:
代碼如下:
while (expression)
statement
在執(zhí)行while語句之前,javascript解釋器首先計算expression的值,如果它的值是假值,那么程序?qū)⑻^循環(huán)體中的邏輯statement轉(zhuǎn)而執(zhí)行程序中的下一條語句。如果它的值是真值,則執(zhí)行循環(huán)體statement內(nèi)的邏輯,然后再計算表達(dá)式expression的值,種循環(huán)會一直持續(xù)下去,知道expression的值為假值為止。換一種說法 就是表達(dá)式為expression是真值的時候則循環(huán)執(zhí)行statement,注意,使用while(true)則會創(chuàng)建一個死循環(huán)。
通常來說,我們不想讓javascript反復(fù)執(zhí)行同一操作。在幾乎每一次循環(huán)中,都會有一個或多個變量隨著循環(huán)而迭代改變。正是由于改變了變量這些變量,因此每次循環(huán)執(zhí)行的statement的操作也不盡相同,而且,如果改變變量在expression中用到,那么每次循環(huán)表達(dá)式的值也不同。這一點非常重要,負(fù)責(zé)初始值為真值的表達(dá)式永遠(yuǎn)是真值,循環(huán)也不會結(jié)束,下面的這個示例所示while循環(huán)輸出0-9值。
代碼如下:
var count = 0;
while (count < 10) {
console.log(count);
count++;
}
可以發(fā)現(xiàn),在這個例子中,變量count的初始值為0,在循環(huán)的過程中,它的值每次都遞增1,當(dāng)循環(huán)執(zhí)行了十次。表達(dá)式的值就編程了false,這時while就會結(jié)束,javascript解釋器將執(zhí)行程序下一條語句。大多循環(huán)都有一個像count這樣的計數(shù)器變量。盡管計數(shù)器常用i j k這樣的變量名,但如果想讓代碼的可讀性更強(qiáng),就應(yīng)當(dāng)使用更具體的語法名。
ii.do/while
do/while循環(huán)和while循環(huán)非常相似,只不過它是在循環(huán)的尾部而不是頂部檢測循環(huán)表達(dá)式,這就意味這循環(huán)體至少執(zhí)行一次。do/while循環(huán)的語法如下:
代碼如下:
do
statement
while(expression);
do/while循環(huán)并不像while循環(huán)那么常用。這是因為在實踐中想要循環(huán)至少執(zhí)行一次的情況并不常見。下面是一個do/while循環(huán)的例子
代碼如下:
function printArray(a) {
var len = a.length,
i = 0;
if (len == 0)
console.log("空數(shù)組");
else
do {
console.log(a[i]);
} while (++i < len);
}
printArray([1,5,2,6])
在do/while循環(huán)和普通while循環(huán)之間有兩點語法方面的不同之處。首先,do循環(huán)要求必須使用關(guān)鍵字do來標(biāo)識循環(huán)的開始,用while變標(biāo)識循環(huán)的結(jié)尾并進(jìn)入循環(huán)條件判斷;其次,和while循環(huán)不同,do循環(huán)使用分號結(jié)尾的。如果while的循環(huán)體使用花括號括起來,則while循環(huán)也不使用分號結(jié)尾。
iii.for
for語句提供了一種比while更方便的循環(huán)語句控制結(jié)構(gòu)。for語句對常用的循環(huán)模式做了一些簡化。大部分的循環(huán)都具有特定的計數(shù)器變量。在循環(huán)開始之前要初始化這個變量,然后在每次循環(huán)之前檢查下它的值。最后,計數(shù)器變量做自增操作,否則就在循環(huán)結(jié)束后、下一次判斷前做修改。在這類循環(huán)中,計數(shù)器的三個關(guān)鍵操作是初始化、檢測和更新。for語句就將這三部操作明確聲明為循環(huán)語法的一部分,各自使用一個表達(dá)式來表示。for語句的語法如下:
代碼如下:
for (initialize; test; increment)
statement
intialize、test、increment三個表達(dá)式之間使用分號分隔,他們負(fù)責(zé)初始化操作、循環(huán)條件判斷和計數(shù)器變量的更新。將它們放在循環(huán)的第一行會更容易理解for循環(huán)正在做什么,而且也可防止忘記初始化或者遞增計數(shù)器變量。
要解釋for循環(huán)是怎么樣工作的,最簡單方法就是列出一個與之等價的while循環(huán)
代碼如下:
initialize
while (test) {
statement
increment;
}
換句話說,initialize表達(dá)式只在循環(huán) 開始之前執(zhí)行一次。初始化表達(dá)式應(yīng)當(dāng)具有副作用(通常是一條賦值語句)。javascript同樣允許初始化表達(dá)式中帶有var變量聲明語句,這樣的話就可以聲明并初始化一個變量。每次循環(huán)之前會執(zhí)行test表達(dá)式,并判斷表達(dá)式的結(jié)果來決定是否執(zhí)行循環(huán)體。每次循環(huán)之前會執(zhí)行test表達(dá)式,并判斷其結(jié)果是否來執(zhí)行循環(huán)體,如果test結(jié)果為真值,則執(zhí)行循環(huán)體中的statement。最后,執(zhí)行increment表達(dá)式。同樣為了有用起見,這里的increment表達(dá)式也必須有副作用。通常來講,它不是一個賦值表達(dá)式就是一個由“++”、“--”運算符構(gòu)成的表達(dá)式。
上文的while循環(huán)可以使用for循環(huán)來從寫
代碼如下:
for (var count = 0; count < 10; count++)
console.log(count)
當(dāng)然,有些循環(huán)更加復(fù)雜,而且循環(huán)中一次迭代多個變量。在javascript,這種情況必須用到逗號運算符,它將初始化表達(dá)式和自增表達(dá)式合并入一個表達(dá)式中以用于for循環(huán)。
代碼如下:
var i, j;
for (i = 0, j = 10; i < 10; i++, j--)
console.log(i * j);
到目前為止,在示例代碼中的循環(huán)變量都是數(shù)字。當(dāng)然是數(shù)字是最常用的,但不是必須的。下面這段代碼就使用for循環(huán)來遍歷表數(shù)據(jù)結(jié)果,并返回鏈表中最后一個對象(也就是第一個不包含next屬性的對象)
代碼如下:
function tail(o) { //返回鏈表的最后一個節(jié)點對象
for (; o.next; o = o.next) /*empty*/ //根據(jù)判斷o.next是不是真值來執(zhí)行遍歷
return o;
}
需要注意的是,這段代碼不包含initialize表達(dá)式,for循環(huán)中的那三個表達(dá)式中的人和一個都可以忽略,但兩個分號必不可少。如果省略test表達(dá)式,那么將是一個死循環(huán)。同樣和while(ture)類型,死循環(huán)的令一種寫法是for(;;)。
iiii.for/in
for/in語句使用for關(guān)鍵字,但它和常規(guī)的for循環(huán)是不同的一類循環(huán)。for/in循環(huán)的語法如下
代碼如下:
for (variable in object)
statement
variable通常是一個變量名,也可以是一個可以產(chǎn)生左值的表達(dá)式或者一個通過var語句聲明的變量。總之是一個適用于賦值表達(dá)式左側(cè)的值。object是一個表達(dá)式,這個表達(dá)式的計算結(jié)果是一個對象。同樣,statement是一個語句或語句塊,它構(gòu)成了循環(huán)的主體。
使用for循環(huán)來遍歷數(shù)組元素是非常簡單的
代碼如下:
var a = [1, 3, 5, "44"];
for (var i = 0; i < a.length; i++) //i代表了數(shù)組元素的索引
console.log(a[i]) //輸出每個數(shù)組的元素
而for/in循環(huán)則是用來方便的遍歷對象成員屬性
代碼如下:
for (var p in o) //將屬性的名字賦值給變量p
console.log(o[p]); //輸出每一個屬性的值
在執(zhí)行 for/in語句的過程中,javascript解釋器首先計算object表達(dá)式。如果表達(dá)式為null或undefined,javascript解釋器將跳過循環(huán)并執(zhí)行后續(xù)的代碼。如果表達(dá)式等于一個原始值,這個原始值將會轉(zhuǎn)換為與之對于的包裝對象(wapper object)(3.6節(jié))。否則,expression本身已經(jīng)是對象了。javascript會依次枚舉對象的屬性來執(zhí)行循環(huán)。然而在每次循環(huán)之前,javascript都會計算variable表達(dá)式的值,并將屬性名(一個字符串)賦值給它。
需要注意的是,只要for/in循環(huán)中,varibale的值可以當(dāng)做賦值表達(dá)式的左值,它可以是任意表達(dá)式。每次循環(huán)都會計算這個表達(dá)式,也就是說每次循環(huán)它計算的值可能不同。例如,可以使用下面的這段代碼將所有對象屬性復(fù)制到一個數(shù)組中:
代碼如下:
var o = {x: 1,y: 2,z: 3};
var a = [],i = 0;
for (a[i++] in o) /*empty*/;
document.write(a)//=> x,y,z
javascript數(shù)組只不過是一種特殊的對象,因此,for/in循環(huán)可以像枚舉對象屬性一樣枚舉數(shù)據(jù)索引。例如在上面的代碼之后添加這段代碼,就可以枚舉數(shù)據(jù)索引0,1,2:
代碼如下:
var o = {x: 1,y: 2,z: 3};
var a = [],i = 0;
for (a[i++] in o) /*empty*/;
document.write(a)//=> x,y,z將對象屬性復(fù)制到一個數(shù)組中
for(i in a)
document.write(i) //=>枚舉數(shù)據(jù)索引 0 1 2
其實,for/in循環(huán)并不會遍歷對象的所有屬性,只有“可枚舉”(enumerable)的屬性才會遍歷到(參照6.7)。由于javascript語言核心所定義的內(nèi)置方法就不是“可枚舉的”。比如,所有的對象都有toString(),但for/in循環(huán)并不枚舉toString()這個屬性。除了內(nèi)置的方法之外,還有很多內(nèi)置對象的屬性是不可枚舉的(nonenumberable)。而代碼中定義的所有屬性和方法都是可枚舉的(6.7節(jié)會講到,但ECMAScript5中有特殊手段可以使屬性變?yōu)椴豢擅杜e)。
對象可以繼承其它對象的屬性,那行繼承自定義屬性(6.2.ii)也可以使用for/in枚舉出來。
如果for/in的循環(huán)體刪除了還未枚舉的屬性,那么這個屬性將不會再枚舉。如果循環(huán)體定義了對象的 新屬性,這些屬性通常也不會枚舉到(不過。javascript有些實現(xiàn)可以枚舉那么些在循環(huán)體中增加的屬性)。
屬性枚舉的順序
ECMAScript規(guī)范并沒有指定for/in循環(huán)按照何種順序來枚舉對象的屬性。但實際上,主流的瀏覽器廠商javascript實現(xiàn)是按照屬性定義的先后順序來枚舉簡單對象的屬性,先定義的屬性先枚舉。如果使用對象直接量的形式創(chuàng)建對象,則將按照直接量中屬性的出現(xiàn)順序枚舉。(有一些網(wǎng)和javascript庫是依賴這種枚舉順序的,而瀏覽器廠商大多不修改這個順序),在下面的情況下,枚舉順序取決于具體的實現(xiàn)(并非交互)
1.對象繼承了可枚舉屬性
2.對象具有整數(shù)數(shù)組索引的屬性
3.使用delete刪除了對象已有的屬性
4.使用Object.defineProperty()或者類似的方法改變了對象屬性
6.跳轉(zhuǎn)
javascript中令一類語句是跳轉(zhuǎn)語句(jump statement)。從語句理解,它可以使javascript執(zhí)行從一個位置跳轉(zhuǎn)到令一個位置。
break語句是跳轉(zhuǎn)到循環(huán)或其他的語句結(jié)束。continue語句是終止本次循環(huán)的執(zhí)行并開始下一次循環(huán)的執(zhí)行。javascript中的語句可以命名或帶有標(biāo)簽,break和continue可以標(biāo)識目標(biāo)循環(huán)或者其它語句標(biāo)簽。
return語句可以讓解釋器跳出函數(shù)體的執(zhí)行。并提供本次調(diào)用的返回值。throw語句觸發(fā)或者拋出一個異常,它是與try/catch/finally語句一同使用的,這些語句指定了處理異常代碼邏輯。這是一種復(fù)雜的跳轉(zhuǎn)語句,當(dāng)拋出一個異常的時候,程序?qū)⑻磷罱拈]合異常辰星,這個異常程序可以是在同一個函數(shù)中或者更高層的調(diào)用棧中。
接下來,描述每一種跳轉(zhuǎn)語句
i.標(biāo)簽語句
語句是可以添加標(biāo)簽的,標(biāo)簽是由語句前的標(biāo)識符和冒號組成:
identifier:statement
通過給語句定義標(biāo)簽,就可以在程序中任何地方通過標(biāo)簽名來引用這條語句??梢詫Χ鄺l語句定義標(biāo)簽,盡管只有給語句塊定義標(biāo)簽時它才有更有用,比如循環(huán)語句或條件判斷語句。通過給循環(huán)定義一個標(biāo)簽名,可以在循環(huán)體內(nèi)部使用break和continue來退出循環(huán)或者直接挑戰(zhàn)到下一個循環(huán)開始。break和continue是javascript中唯一可使用語句標(biāo)簽的語句(本章接下來會講述)。下面的例子,其中while循環(huán)定義了一個標(biāo)簽,continue語句使用了這個標(biāo)簽:
代碼如下:
mainloop: while (token != null) {
//忽略這里代碼...
continue mainloop; //跳轉(zhuǎn)到下一次循環(huán)
//忽略這里的代碼...
}
這里做標(biāo)簽的indentifier必須是一個合法的javascript標(biāo)識符,而不能是一個保留字。標(biāo)簽的命名空間和變量或函數(shù)的命名空間是不同的,因此可以使用同一個標(biāo)識符作為語句標(biāo)簽和作為變量名或函數(shù)名。語句標(biāo)簽只在它所起作用的語句(當(dāng)然可以在它的子句)內(nèi)是有定義的。一個語句標(biāo)簽不能和它內(nèi)部的語句標(biāo)簽重名,但在兩個代碼不相互嵌套的情況下是可以出現(xiàn)同名語句標(biāo)簽的。帶有標(biāo)簽的語句還可以帶有標(biāo)簽,也就是說,任何語句可以有很多個標(biāo)簽。
ii.break
單獨使用break語句的作用是立即退出最內(nèi)存的循環(huán)或switch語句。它的語法如下:
break;
由于它能夠使循環(huán)和switch語句退出,因此這種形式的break只能出現(xiàn)在這類語句中才是合法的。
我們在switch語句的例子中已經(jīng)見到果break語句。在循環(huán)中,無論出于什么原因,只要不想繼續(xù)執(zhí)行整個循環(huán),就可以用break提前退出。當(dāng)循環(huán)終止條件非常復(fù)雜時,要函數(shù)體內(nèi)使用break語句實現(xiàn)這樣些條件判斷的做法要比直接在循環(huán)表達(dá)式中寫出這個復(fù)雜的終止條件做法簡單的多。
下面的例子中循環(huán)遍歷整個數(shù)組元素來查找某個特定的值,當(dāng)整個數(shù)組遍歷完成后正常退出循環(huán),如果找到 了需要查找的數(shù)組元素,則使用break語句退出循環(huán):
代碼如下:
for (var i = 0; i < a.length; i++) {
if (a[i] == target) break;
}
javascript中同樣允許break關(guān)鍵字后跟隨一個語句標(biāo)簽,(只有標(biāo)識符,沒有冒號)
break labelname;
當(dāng)break和標(biāo)簽一塊使用時,程序?qū)⑻D(zhuǎn)到這個標(biāo)簽所識別的語句塊的結(jié)束,或者直接終止這個閉合語句塊的執(zhí)行。當(dāng)沒有任何閉合語句塊指定break所用的標(biāo)簽,這時會產(chǎn)生一個語法錯誤。當(dāng)使用這種形式的break語句時,帶標(biāo)簽的語句不應(yīng)該是循環(huán)或者switch語句,因為break語句可以“跳出”任何閉合的語句塊。這里的語句可以是由花括號組起來的一組語句,使用同一個標(biāo)簽來識別一組語句。
break關(guān)鍵字和labelname之間不能換行。因為javascript可以給語句自動補(bǔ)全省略掉的分號,如果break關(guān)鍵字和標(biāo)簽之間有換行,javascript解釋器會認(rèn)為你在使用break不帶標(biāo)簽的最簡形式,因此會在break后補(bǔ)充分號.
當(dāng)你希望通過break來跳出非就近的循環(huán)體或者switch語句時,就會用到帶標(biāo)簽的break語句。下面是示例代碼:
代碼如下:
var matrix = getData(); //從某處獲得一個二維數(shù)組
//將矩陣中所有元素進(jìn)行求和
var sum = 0,
success = false;
//從簽名處開始,以便在報錯時推出程序。
compure_sum: if (matrix) {
for (var x = 0; x < matrix.length; x++) {
var row = matrix[x];
if (!row) break compure_sum;
for (var y = 0; y < row.length; y++) {
var cell = row[y];
if (isNaN(cell)) break compure_sum;
sum += cell;
}
}
success = true;
}
//break語句跳轉(zhuǎn)至此
//如果success =false條件到達(dá)這里,說明我們給出的矩陣中有錯誤
//否則對矩陣中所有的元素進(jìn)行求和
最后,需要注意的是,不管break語句帶不帶標(biāo)簽,它的控制權(quán)都無法越過函數(shù)的邊界。比如:對于一條帶標(biāo)簽的函數(shù)定義語句來說,不能通過函數(shù)內(nèi)部通過這個標(biāo)簽來跳轉(zhuǎn)到函數(shù)外部.
iii.continue語句
continue語句和break語句非常類似,但它不退出循環(huán),而是轉(zhuǎn)而執(zhí)行下一次循環(huán)。continue語句的語法和break的語句語法一樣簡單
continue;
continue語句會也會帶有標(biāo)簽
continue lebname;
不管continue語句帶不帶標(biāo)簽,它只能在循環(huán)體使用,在其它地方使用將會 報語法錯誤。
當(dāng)執(zhí)行到continue語句的時候,當(dāng)前的循環(huán)邏輯就終止了,隨即執(zhí)行下一次循環(huán),在不同類型的循環(huán)中,continue的行為也有區(qū)別
1. 在while循環(huán)中,在循環(huán)開始處指定expression會重復(fù)檢測,如果檢測結(jié)果為true,循環(huán)體會從頭執(zhí)行。
2. 在do/while循環(huán)中,程序的執(zhí)行至今跳轉(zhuǎn)到循環(huán)的結(jié)尾處,這時會重新判斷循環(huán)條件,之后才會繼續(xù)下一次循環(huán)。
3. 在for循環(huán)中,首先會計算自增表達(dá)式,然后再檢測test表達(dá)式,用以判斷是否執(zhí)行循環(huán)體。
4. 在for/in循環(huán)中,循環(huán)開始遍歷下一個屬性名,這個屬性名賦給了指定的變量。
需要注意continue語句在while和for循環(huán)中的區(qū)別,while循環(huán)直接進(jìn)入下一輪的循環(huán)條件判斷,但for循環(huán)首先計算器increment表達(dá)式,然后判斷循環(huán)條件。之前的章節(jié)討論了和while循環(huán)“等價”的for循環(huán)行為。但由于continue在這兩種循環(huán)中行為表現(xiàn)不同,因此使用while循環(huán)不可能完美的模擬等價的for循環(huán)。
下面這段代碼展示了不帶標(biāo)簽的continue語句,產(chǎn)生一個錯誤的時候跳過當(dāng)前循環(huán)的后續(xù)邏輯
代碼如下:
for (i = 0; i < data.length; i++) {
if (!data[i]) continue; //不能處理undefined數(shù)據(jù)
total += data[i];
}
和break語句類似,帶標(biāo)簽的continue語句可以用在嵌套的循環(huán)中,用以跳出層次嵌套的循環(huán)體邏輯。同樣和break語句類似,在continue語句和labname之間不能有換行。
iiii.return
回想一下,函數(shù)調(diào)用的一種表達(dá)式,而且所有的表達(dá)式都有值。函數(shù)中的return語句即是指函數(shù)調(diào)用后的返回值。這里是return語句的語法:
return expression;
return語句只能在函數(shù)體內(nèi)出現(xiàn),如果不是的話會報語法錯誤。當(dāng)執(zhí)行到return語句的時候,函數(shù)終止執(zhí)行,并返回expression的值給調(diào)用程序。例如:
代碼如下:
function square(x) {return x * x} //一個包含return的語句函數(shù)
square(4) //執(zhí)行為16
如果沒有return語句,則函數(shù)調(diào)用僅依次執(zhí)行函數(shù)體內(nèi)的每一條語句直到函數(shù)結(jié)束,最后返回調(diào)用程序。這種情況下,調(diào)用表達(dá)式的結(jié)果是undefined。return語句經(jīng)常作為函數(shù)內(nèi)最后的一條語句出現(xiàn),但并不是說一定一定要放在函數(shù)的最后,即使在執(zhí)行return語句的時候還有很多代碼沒有執(zhí)行到,這時候函數(shù)也還返回調(diào)用程序。
return語句可以單獨使用而不必帶有expression,這樣的話函數(shù)也會想調(diào)用程序返回undefined.例如:
代碼如下:
//如果參數(shù)是null或者undefined則立即返回
if (!o) return;
//其它邏輯
由于javascript可以自動插入分號,因此,return關(guān)鍵字和它后面的表達(dá)式之間不能有換行。
iiiii.throw語句
所謂異常(excepion)是當(dāng)發(fā)生了某種異常情況或錯誤時產(chǎn)生的一個信號。拋出異常,就是用信號通知發(fā)生了錯誤或異常狀況。捕獲異常是指處理這個信號,拋出異常,就是用信號通知發(fā)生了錯誤或異常狀況。捕獲異常是指處理這個信號,即采取必要的手段從異常中匯豐。在javascript中,當(dāng)產(chǎn)生運行時錯誤或者程序使用throw語句時就會顯式的拋出異常。使用try/catch/finally語句可以捕獲異常,下一節(jié)會對它作詳細(xì)介紹。
throw語句的語法如下:
throw expression
expression的值可以是任意類型的。可以拋出一個代表錯誤碼的數(shù)組,或者包含可錯誤消息的字符串。當(dāng)javascript解釋器拋出異常的時候,通常采用Eeeor類型或其子類型,當(dāng)然也可以使用它們。一個error對象有一個那么熟悉表示錯誤類型,一個message屬性用來傳遞構(gòu)造函數(shù)的字符串(參照第三部分的Error類),在下面的例子中,當(dāng)使用非法參數(shù)調(diào)用函數(shù)時就拋出一個Error對象:
代碼如下:
function fa(x) {
//如果輸入的參數(shù)是非法的,則拋出一個異常
if (x < 0) throw new Error("x不能是負(fù)數(shù)。");
//否則計算出一個值,正常地返回它
for (var f = 1; x > 1; f *= x, x--) /*empty*/;
return f;
}
當(dāng)拋出異常時,javascript解釋器會立即停止當(dāng)前正在執(zhí)行的邏輯,并跳轉(zhuǎn)至就近的異常處理程序。異常處理程序用try/catch/finally語句的catch從句編寫的。如果拋出的異常沒有一條關(guān)聯(lián)catch從句 ,解釋器會檢測更高層的閉合代碼塊,看它是否關(guān)聯(lián)相關(guān)的異常處理程序。以此類推 ,直到扎到一個異常處理的程序為止。
如果拋出的異常函數(shù)沒有處理它的try/catch/finally語句,異常將向上傳播到調(diào)用該函數(shù)的代碼。這樣的話,異常就會沿著javascript方法的詞法結(jié)構(gòu)和調(diào)用棧向上傳播。如果沒有找到任何異常處理的程序,javascript將吧異常當(dāng)成程序錯誤來處理,并報告給用戶。
iiiiii.try/catch/finally語句
try/catch/finally語句是javascript的異常處理機(jī)制。其中try從句定義了需要處理的異常所在代碼塊。catch語句跟隨在try從句之后,當(dāng)try塊從某處發(fā)送了異常時,調(diào)用了catch內(nèi)的代碼邏輯。catch從句跟隨finnlly塊,后者防置了清理代碼,不管try塊中是否產(chǎn)生了異常,finnally塊內(nèi)的邏輯總會執(zhí)行。盡管catch和finally都是可選的,但try從句只殺二者之一與組成完整的語句。try、catch和finally語句塊都需要花括號括起來,這里的花括號是必須的。即使從句中只有一條語句也不能省略花括號。
下面的代碼說明了try/catch/finlly的語法和使用目的:
代碼如下:
try{
//通常來講,這里的代碼會從頭執(zhí)行到尾而不會產(chǎn)生任何問題,
//但有時會拋出一個異常,要么是由throw語句直接拋出異常
//要么通過調(diào)用一個方法間接拋出異常
}
catch(e){
//當(dāng)且僅當(dāng)try拋出了異常,才會執(zhí)行這里的代碼
//這里可以通過局部變量e來獲得對Error對象或者拋出的其它值的引用
//這里的代碼可以基于某種原因處理這個異常 ,也可以忽略這個異常。
//還可以通過throw語句重新拋出異常
}
finally{
//不管try語句塊是否拋出看異常,這里的邏輯總會執(zhí)行,終止try的語句塊方式有:
//1)正常終止,執(zhí)行完語句塊的最后一條語句
//2)通過break,continue或return語句終止
//3)拋出一個異常,異常被catch從句捕獲
//4)拋出一個異常,異常未被捕獲,繼續(xù)向上傳播
}
我們注意到,關(guān)鍵字catch后跟隨了一對圓括號,圓括號內(nèi)是一個標(biāo)識符。這個標(biāo)識符和函數(shù)參很像。當(dāng)捕獲一個異常時,把這個異常相關(guān)的值(比如Error對象)賦值給這個參數(shù)。和普通的變量不同,這條catch子句中的標(biāo)識符具有塊級作用域,它只在catch語句塊 內(nèi)有定義。
這里有一個關(guān)于try/catch語句更實際的例子,這里使用了前面章節(jié)中提到factorial()方法,并使用客戶端javascript方法prompt()和alert()來輸入和輸出
代碼如下:
try {
//要求用戶輸入一個數(shù)字
var n = Number(prompt("請輸入一個正整數(shù)", ""));
//假設(shè)輸入是合法的,計算這個階乘
var f = factorial(n);
//顯示結(jié)果
alert(n + "!=" + f);
} catch (ex) {
//如果輸入不合法,將執(zhí)行這里的邏輯
document.write(ex); //告訴用戶發(fā)送了什么。
}
這里的try/catch語句并不包含finally從句。盡管finally不像catch那樣經(jīng)常使用,但有時候它還是非常有用。然而,我們需要更詳盡的解釋它的行為。不管try語句塊中的代碼執(zhí)行完成了多少,只要try語句中有一部分代碼執(zhí)行了,finally從句就會執(zhí)行。它通常在try從句的代碼后用于清理工作。
關(guān)注下面這個例子
代碼如下:
try {
print("Outer try running..");
try {
print("Nested try running...");
throw "an error";
} catch (e) {
print("Nested catch caught " + e);
throw e + " re-thrown";
} finally {
print("Nested finally is running...");
}
} catch (e) {
print("Outer catch caught " + e);
} finally {
print("Outer finally running");
}
// Windows Script Host 作出該修改從而得出 WScript.Echo(s)
function print(s) {
document.write(s);
}
輸出:
代碼如下:
Outer try running..
Nested try running...
Nested catch caught an error
Nested finally is running...
Outer catch caught an error re-thrown
Outer finally running
7.其它語句類型。
本節(jié)討論剩余的三種javascript語句:width,debugger和use strict
i.with語句
3.10討論了作用域鏈(scope chain),一個可以按序檢索的對象列表,通過它可以進(jìn)行變量名的解析。width語句可以用來臨時擴(kuò)展作用域鏈:它具體有如下語法:
with (object)
statement
這條語句將object添加到作用域鏈頭部,然后執(zhí)行statement,最后把作用域鏈恢復(fù)到原始狀態(tài)。
在嚴(yán)格模式下(5.7.iii)是禁止使用width的,在非嚴(yán)格模式下也是不推薦使用width語句的,盡可能的避免使用width語句。那些使用width語句的javascript非常難優(yōu)化,而且比沒有使用width的語句,它運行速度更慢。
在對象嵌套層次很深的時候,常會使用with語句來簡化代碼的編寫。例如客戶端javascript中,可能使用下面的這種表達(dá)式來訪問表單的一個html元素
document.forms[0].address.value
如果這段代碼多次出現(xiàn),則可以使用with將form對象添加至作用域鏈的頂層。
代碼如下:
with(document.forms[0]){
//直接訪問表單元素
name.value="";
address.value="";
email.value ="";
}
這種方法簡化了大量的輸入,不用再為每個變量添加document.forms[0]前綴。這個臨時對象掛載在作用域鏈上,當(dāng)javascript需要解析諸如address標(biāo)識符時,就會在這個對象中查找。當(dāng)然,不使用with的語句代碼可以寫成這樣。
代碼如下:
var f = document.forms[0];
f.name.value = "";
f.adress.value = "";
f.email.value = "";
不要忘記,只有在查找標(biāo)識符的時候才能用到作用域鏈,創(chuàng)建新的變量時候不使用它,看一下下面的代碼:
代碼如下:
with(o) x = 1;
如果對象o有一個屬性x,那么這行代碼給這個屬性賦值1。如果o沒有定義屬性x,這段代碼和不使用with的代碼x=1是一模一樣的。它給一個局部變量或者全局變量x賦值,或者創(chuàng)建全局對象的一個新屬性。with語句提供了一種讀取o屬性的快捷方法,但并不會創(chuàng)建o的屬性。
ii.debugger語句
debugger語句通常什么也不做。然而,在調(diào)試程序可用并運行的時候,javascript解釋器將會(非必須)以調(diào)試模式運行。實際上,這條語句產(chǎn)生一個斷點(breakpoint),javascript代碼執(zhí)行會停止在斷點的位置,這時可用使用調(diào)速器輸出變量的值,檢查調(diào)用棧等。
例如加上調(diào)用函數(shù)f()的時候使用了未定義的參數(shù),因此f()拋出一個異常,但無法定位到到底哪里出了異常。為了有助于調(diào)試這個問題,需要修改f():
代碼如下:
function f(o){
if (o === undefined) debugger; //這段代碼用來臨時調(diào)試
console.log(1) //函數(shù)的其它部分
}
f();
這時候,當(dāng)調(diào)用f()沒有傳入?yún)?shù),程序?qū)⑼V箞?zhí)行,這時候通過調(diào)用調(diào)速器檢測調(diào)用棧并找出錯誤的原因。
在ECMAScirpt5中,debugger語句已經(jīng)正式加入到專門語言里,但在很長的一段時間里,主瀏覽器的廠商已經(jīng)將其實現(xiàn)了。注意,可用的調(diào)速器是遠(yuǎn)遠(yuǎn)不夠的,debugger語句不會啟動調(diào)試器。但如果調(diào)試器已經(jīng)在運行,這條語句才會正在產(chǎn)生斷點。例如,使用Firefox插件firebug,首先啟動firebug,這樣debugger語句才能工作。
iii.“use strict”
“use strict”是ECMASCript5引入的一條指令。指令不是語句(但非常接近于語句),“use strict”和普通語句之前有兩個重要區(qū)別:
1.它不包含任何語言的關(guān)鍵字,指令僅僅是一個包含一個特殊字符串直接量的表達(dá)式(可以是使用單引號也可以是雙引號)。
2.它只能出現(xiàn)在腳本代碼的開始或者函數(shù)體的開始、任何實體語句之前。但它不必一定出現(xiàn)在腳本的首行或者函數(shù)體內(nèi)的首行。因為“use strict”指令之前之后或之前都可能有其它字符串直接量的表達(dá)式語句,并且javascript的具體實現(xiàn)可能將它們解析為解釋器自有的指令。在腳本或者函數(shù)體內(nèi)第一條常規(guī)語句之后,字符串直接量表達(dá)式語句只當(dāng)做普通的表達(dá)式語句對待,它們不做指令解析,它們也沒有任何副作用。
使用“use strict”指令的目的是說明(腳本或函數(shù)中)后續(xù)代碼解析為嚴(yán)格代碼(strict code)。如果頂層(不在任何函數(shù)內(nèi))代碼使用了“use strict”指令,那么它們就是嚴(yán)格代碼。如果函數(shù)體定義處的代碼是嚴(yán)格代碼或者函數(shù)體使用了“use strict”指令,那么函數(shù)體的代碼也是嚴(yán)格代碼。如果eval()調(diào)用所處的代碼是嚴(yán)格代碼或者eval()要執(zhí)行的字符串使用了“scrict code”指令,則eval()內(nèi)的代碼是嚴(yán)格代碼。
嚴(yán)格代碼以嚴(yán)格模式執(zhí)行。ECMAScript5中的嚴(yán)格模式是該語言的一個受限的子集。它修正了語言的重要缺陷,并提供健壯的差錯功能和增強(qiáng)安全機(jī)制。嚴(yán)格模式和非嚴(yán)格模式區(qū)別如下(前三條尤其重要)
•嚴(yán)格模式中禁止使用with語句
•嚴(yán)格模式中,所有的變量要先聲明,如果給一個未聲明的變量、函數(shù)、函數(shù)參數(shù)、catch從句參數(shù)或全局的對象的屬性賦值。就會拋出一個引用錯誤異常(在非嚴(yán)格模式中,這種隱式聲明全局變量的方法是給全局變量新添加一個新屬性)
•嚴(yán)格模式中,調(diào)用的函數(shù)(不是方法)中的一個this值是undefined。(在非嚴(yán)格模式中,調(diào)用的函數(shù)中的this值總是全局變量)??梢岳眠@種特性來判斷javascript實現(xiàn)是否支持嚴(yán)格模式。
代碼如下:
var hasStrictMode = (function() {
"use strict";
return this === undefined
}());
•同樣,在嚴(yán)格模式中,當(dāng)通過call()和apply()來調(diào)用函數(shù)時,其中的this值就是通過call()或apply()傳第一個參數(shù)(在非嚴(yán)格模式中,null和undefined值被全局對象轉(zhuǎn)換為對象的非對象值鎖代替)
•在嚴(yán)格模式中,給只讀屬性賦值和給不可擴(kuò)展的對象創(chuàng)建成員都將拋出一個類型錯誤異常(在非嚴(yán)格模式中,這些操作只是簡單的操作失敗,不會報錯)。
•在嚴(yán)格模式中,傳入eval()代碼不能再調(diào)用辰星所在的上下文中聲明變量或定義函數(shù),在非嚴(yán)格模式中是可以這樣做的。相反,變量和函數(shù)的定義是在eval()創(chuàng)建的作用域中,這個作用域在eval()返回時就棄用了。
•在嚴(yán)格模式中,函數(shù)里的arguments對象擁有傳入函數(shù)值的靜態(tài)副本。在非嚴(yán)格模式中,agreements對象具有“魔術(shù)般”的行為,arguments里的數(shù)組元素和函數(shù)都指向同一個值的引用。
•在嚴(yán)格模式中,當(dāng)delete運算符后面跟隨非法的標(biāo)識符(比如變量、函數(shù)、函數(shù)參數(shù)時)將會拋出一個語法錯誤,(在非嚴(yán)格模式下,這種delete什么也沒做,并返回false)
•在嚴(yán)格模式中,在一對象直接量中定義兩個或多個同名屬性將產(chǎn)生一個語法錯誤(非嚴(yán)格模式下不會報錯)
•在嚴(yán)格模式下,不允許八進(jìn)制整數(shù)直接量。(以0為前綴,而不是0x為前綴)在非嚴(yán)格模式中是允許直接八進(jìn)制直接量的
•在嚴(yán)格模式下,標(biāo)識符eval和arguments當(dāng)做關(guān)鍵字,他們的值是不能更改的。不能給這些標(biāo)識符賦值,也不能把它們聲望為變量,用做函數(shù)名,用做函數(shù)參數(shù)或用做catch塊的標(biāo)識符。
•在嚴(yán)格模式中限制了對調(diào)用棧的檢測能力,在嚴(yán)格的模式的函數(shù)中,arguments,caller和arguments.callee都會拋出一個類型錯誤異常。嚴(yán)格模式的函數(shù)同樣具有caller和arguments屬性,當(dāng)訪問這兩個屬性時拋出類型錯誤異常。
8.javascript語句小結(jié):
javascript語句語法:
語句語法用途
breakbreak[label];退出最內(nèi)側(cè)循環(huán)或者退出switch語句,又或退出label指定的語句
casecase expression:在switch語句標(biāo)記一條語句
continuecontinue [label];重新開始最內(nèi)層的循環(huán)或從新開始label指定的循環(huán)
debuggerdebugger;斷點器調(diào)試
defaultdefault;在switch標(biāo)記默認(rèn)語句
do/whiledo statement while(expression);while循環(huán)的一種替代形式
empty;什么都不做
forfor(init;test;incr)statement一種簡寫的循環(huán)
for/infor(var in object)statement遍歷一個對象屬性
functionfunction name([param[],...]){body}聲明一個函數(shù)
if/elseif (expr)statement1[else statement2]執(zhí)行statement1或者statement2
labellabel:statement給statement指定一個名字:label
returnreturn [expression];從函數(shù)返回一個值
switchswitch(expression){statements}用case或者“default:”語句標(biāo)記多個分支語句
throwthrow expression拋出異常
trytry {statements}
[catch {hander satements}]
[finally {cleanup satements}]捕獲異常
use strict"use strict"對腳本和函數(shù)使用嚴(yán)格模式
varavr name=[=expr][,...]聲明并初始化一個或多個變量
whilewhile (expression) statement基本的循環(huán)結(jié)構(gòu)
withwith(object) statement擴(kuò)展作用域鏈(不贊成使用)
更多信息請查看IT技術(shù)專欄