1. 引言
假設(shè)有這么一道題:
for (var i = 0; i < 10; i++) {
console.log(i);
for (var j = 0; j < 5; j++) {
console.log(j);
}
}
console.log('done');
我想要當 j = 2 的時候就退出所有的for語句瑞侮,打印最后的 done 谊娇,你會怎么做类垫?
可能有的同學(xué)會想到這樣:
function foo () {
for (var i = 0; i < 10; i++) {
console.log(i);
for (var j = 0; j < 5; j++) {
console.log(j);
if (j === 2) return;
}
}
}
foo();
console.log('done');
這樣可以實現(xiàn),但是又多寫了一個函數(shù)惹悄,那么有沒有別的辦法呢?
再看一個例子肩钠,你也一定見到過這樣的寫法:
// 假設(shè)str是你通過ajax接收到的JSON串
var str = '{"name": "liu", "age": 20}';
var obj = eval('(' + str + ')');
console.log(obj);
那么泣港,你有沒有想過 eval 里面為什么要加上括號呢?如果不加又是什么情況价匠?(提前劇透当纱,不加括號這里會報錯哦)。
接著往下看踩窖,當你讀完這篇文章的時候坡氯,心中的疑惑會完全解開。
2. Label Statement
學(xué)過C語言的同學(xué)知道洋腮,C的語法中有一個語句叫:goto箫柳,同時老師也多次強調(diào)不讓我們使用goto語句,因為會大大影響程序的可讀性和可維護性啥供。
我們先來看一段C語言的goto代碼:
void main(){
int a=2, b=3;
if(a>b) {
goto aa;
}
printf("hello");
aa: printf("s");
return 0;
}
當 a < b 的時候滞时,這里會打印字符串 "hello",然后結(jié)束滤灯。
當 a > b 的時候坪稽,由于goto語句的作用,就會跳過 print("hello")鳞骤,直接跳到 aa 標簽聲明的代碼塊中窒百,打印字符 "s",然后結(jié)束豫尽。
這就是goto語句的作用篙梢,通過標簽聲明一個代碼塊,然后在任何地方都可以執(zhí)行 goto 'labe' 來進行程序跳轉(zhuǎn)美旧。
顯而易見渤滞,這樣的寫法,違背了程序順序執(zhí)行的原則榴嗅,會跳來跳去妄呕,最后導(dǎo)致根本無法維護,所以嗽测,記住老師的話绪励,不要使用 goto 語句肿孵。
那么,看完了C語言中的 goto 語句疏魏,和我們的 JavaScript 又有什么關(guān)系呢停做?
這就引出了今天的主題:Label Statement,它就是 JS 中的 goto 語句大莫。
3. 用法
首先明確一個原則蛉腌,在JavaScript中,語句優(yōu)先只厘。
也就是說眉抬,如果一段代碼既能夠以語句的方式解析,也能用語法的方式解析懈凹,在JS中蜀变,會優(yōu)先按語句來解析。
{ a : 1 }
上面這段代碼介评,在JS中的執(zhí)行結(jié)果是什么呢库北?
大家思考2分鐘....
好,2分鐘已過们陆,大家有結(jié)果了嗎寒瓦?
千萬不要在瀏覽器的控制臺中去寫這段代碼,雖然結(jié)果和你開始想的結(jié)果一樣坪仇,
但是杂腰,它是錯誤的。
這是在console控制臺中執(zhí)行的結(jié)果:
這是在watch中的執(zhí)行結(jié)果:
可以看到兩個結(jié)果是不一樣的椅文。
console是經(jīng)過處理的這里不能相信喂很,watch是直接JS的運行環(huán)境執(zhí)行后的結(jié)果,是正確的皆刺。
為什么 { a : 1 } 結(jié)果會是 1 呢少辣?
我換一個寫法:
{
a : 1
}
相信有的同學(xué)已經(jīng)明白了,在JS中羡蛾,{}既可以代表代碼塊漓帅,又可以作為Object的語法標志。
那么我們前面說過痴怨,JS是語句優(yōu)先的忙干,當一段代碼既可以按照語句解析,又可以按照語法解析的時候浪藻,會優(yōu)先按語句解析捐迫。
當把{}當做是代碼塊的時候,里面的 a : 1珠移,是不是很像C語言goto語句的標簽聲明呢弓乙?
開頭我們提出的第一個問題,如果用這種方式來解決钧惧,代碼如下:
aa : {
for (var i = 0; i < 10; i++) {
console.log(i);
for (var j = 0; j < 5; j++) {
console.log(j);
if (j === 2) break aa;
}
}
}
console.log('done');
aa是標簽聲明暇韧,包裹一個代碼塊,break 的作用是跳出當前的循環(huán)浓瞪,本來是無法跳出外面那層for循環(huán)的懈玻,但是 break aa,這里跳出了整個代碼塊乾颁。
當然涂乌,這種寫法是完全不提倡的,這里只是用來說明JS中的Label Statement這個特性英岭,大家千萬不要這樣寫代碼湾盒。
再來看開頭提出的第二個問題:
// 假設(shè)str是你通過ajax接收到的JSON串
var str = '{"name": "liu", "age": 20}';
var obj = eval('(' + str + ')');
console.log(obj);
我們知道,eval(str)會把接收到的字符串在當前上下文中執(zhí)行诅妹,如果不加括號:
eval('{"name": "liu", "age": 20}}')
這里的執(zhí)行語句就會變成:
{
"name" : "liu", "age" : 20
}
{}按照語句解析罚勾,執(zhí)行里面的逗號表達式,我們知道逗號表達式要求每一項都必須是表達式吭狡,輸出最后一項的結(jié)果尖殃,而這里不滿足要求,所以會報錯划煮。
但是加上括號就變成了這樣:
({
"name" : "liu", "age" : 20
})
小括號可以把里面的內(nèi)容當做表達式來解析送丰,那么里面的內(nèi)容就是一個對象了。
這也是立即執(zhí)行函數(shù)的原理:
(function () {
console.log('IIFE');
})()
小括號把函數(shù)聲明變成了函數(shù)表達式弛秋,后面再跟一個小括號表示調(diào)用器躏。
4. 結(jié)束
這里通過幾個例子,引出了 JavaScript 的標簽聲明語句(Label Statement)蟹略,從而解釋了一些我們常用寫法的原理邀桑。
以后萬一有人問你為什么 eval() 解析JSON要加括號呢?
這回知道怎么說了吧科乎。