JS學習系列 07 – 標籤聲明(Label Statement)

原文: JS學習系列 07 – 標籤聲明(Label Statement)

 

1. 引言

假設有這麼一道題:

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 ,你會怎麼做?

可能有的同學會想到這樣:

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');

這樣可以實現,但是又多寫了一個函數,那麼有沒有別的辦法呢?

再看一個例子,你也一定見到過這樣的寫法:

// 假設str是你通過ajax接收到的JSON串
var str = '{"name": "liu", "age": 20}';
var obj = eval('(' + str + ')');

console.log(obj); 

那麼,你有沒有想過 eval 裏面爲什麼要加上括號呢?如果不加又是什麼情況?(提前劇透,不加括號這裏會報錯哦)。

接着往下看,當你讀完這篇文章的時候,心中的疑惑會完全解開。

2. Label Statement

學過C語言的同學知道,C的語法中有一個語句叫:goto,同時老師也多次強調不讓我們使用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",然後結束。
當 a > b 的時候,由於goto語句的作用,就會跳過 print("hello"),直接跳到 aa 標籤聲明的代碼塊中,打印字符 "s",然後結束。

這就是goto語句的作用,通過標籤聲明一個代碼塊,然後在任何地方都可以執行 goto 'labe' 來進行程序跳轉。

顯而易見,這樣的寫法,違背了程序順序執行的原則,會跳來跳去,最後導致根本無法維護,所以,記住老師的話,不要使用 goto 語句

那麼,看完了C語言中的 goto 語句,和我們的 JavaScript 又有什麼關係呢?
這就引出了今天的主題:Label Statement,它就是 JS 中的 goto 語句。

3. 用法

首先明確一個原則,在JavaScript中,語句優先
也就是說,如果一段代碼既能夠以語句的方式解析,也能用語法的方式解析,在JS中,會優先按語句來解析。

{ a : 1 }

上面這段代碼,在JS中的執行結果是什麼呢?
大家思考2分鐘....

-----------

好,2分鐘已過,大家有結果了嗎?
千萬不要在瀏覽器的控制檯中去寫這段代碼,雖然結果和你開始想的結果一樣,
但是,它是錯誤的。

這是在console控制檯中執行的結果:

label-console圖片

這是在watch中的執行結果:

clipboard.png

可以看到兩個結果是不一樣的。
console是經過處理的這裏不能相信,watch是直接JS的運行環境執行後的結果,是正確的。

爲什麼 { a : 1 } 結果會是 1 呢?

我換一個寫法:

{
    a : 1
}

相信有的同學已經明白了,在JS中,{}既可以代表代碼塊,又可以作爲Object的語法標誌。
那麼我們前面說過,JS是語句優先的,當一段代碼既可以按照語句解析,又可以按照語法解析的時候,會優先按語句解析。

當把{}當做是代碼塊的時候,裏面的 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 的作用是跳出當前的循環,本來是無法跳出外面那層for循環的,但是 break aa,這裏跳出了整個代碼塊。

當然,這種寫法是完全不提倡的,這裏只是用來說明JS中的Label Statement這個特性,大家千萬不要這樣寫代碼。

再來看開頭提出的第二個問題:

// 假設str是你通過ajax接收到的JSON串
var str = '{"name": "liu", "age": 20}';
var obj = eval('(' + str + ')');

console.log(obj); 

我們知道,eval(str)會把接收到的字符串在當前上下文中執行,如果不加括號:

eval('{"name": "liu", "age": 20}}')

這裏的執行語句就會變成:

{
    "name" : "liu", "age" : 20
}

{}按照語句解析,執行裏面的逗號表達式,我們知道逗號表達式要求每一項都必須是表達式,輸出最後一項的結果,而這裏不滿足要求,所以會報錯。

label-watch2

但是加上括號就變成了這樣:

({
    "name" : "liu", "age" : 20
})

小括號可以把裏面的內容當做表達式來解析,那麼裏面的內容就是一個對象了。

label-watch2

這也是立即執行函數的原理:

(function () {
    console.log('IIFE');
})()

小括號把函數聲明變成了函數表達式,後面再跟一個小括號表示調用。

4. 結束

這裏通過幾個例子,引出了 JavaScript 的標籤聲明語句(Label Statement),從而解釋了一些我們常用寫法的原理。

以後萬一有人問你爲什麼 eval() 解析JSON要加括號呢?
這回知道怎麼說了吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章