因爲 Vue Ref 提案,我又刷了遍 label 語法

label 0x0:Vue Ref 提案

近日,Vue 作者在社區提交了一份Ref 語法糖的提案,引發了社區的爭議。

關於 Ref 提案,它是將原本應該使用 ref() 調用的方式,通過 label 語法 ref: 進行簡化編寫,並在編譯期間進行轉換,所以這個本質上是個語法糖,示例如下,

原寫法:

1
2
3
4
5
6
7
8







<script setup>
...
const count = ref(0) // ref調用
const inc = () => { count.value++ }
</script>
<template>
<Foo :count="count" @click="inc" />
</template>







新提案寫法:

1
2
3
4
5
6
7
8
9
10









<script setup>
...
ref: count = 1 // 使用label語法
function inc() {
count++
}
</script>
<template>
<button @click="inc">{{ count }}</button>
</template>









這個提議最大的爭議在於,新提案的寫法是符合 JS 語法,但卻不符合 JS 的語義,按照 label 的語義,ref: count = 0 裏的 ref: 是無任何意義的,既在運行時不做解析,但在 Vue 的 script setup 下卻賦予了 ref() 的功能!

label 語法通常很少被使用,以至於部分人在看到新提案的寫法後認爲是 Vue 的新魔法,紛紛表示學不動或認爲應該將 Vue 的寫法叫 VueScript。

在看到 ref 提案後又回想起多年來未曾用過的 label 語法,本篇主要介紹及回顧下彙編、C、JavaScript 語言的 label 語法和使用。

label 0x1:彙編語言

彙編語言主要是彙編指令(語句)組成,一條指令有四個組成部分,如下:

1
2

[label: ] mnemonic [operands] [;comment]
標號:(可選)+ 指令助記符(必需)+ 操作數(通常是必需的)+ 註釋(可選)

示例:

1
L0: mov ax,0; 這是一條彙編指令

標號(label)是一種標識符,是指令的位置標記。標號位於指令的前端,表示指令的地址。

默認情況下,CPU 是順序加載並執行程序。但是,在實現類似條件跳轉、循環等功能時,就需要使用跳轉指令+lablel 來實現,彙編語言中有多種跳轉指令,這裏以 jmp 爲例,語法如下:

1
jmp label(目標地址)

示例一:

1
2
3
4



L1:
mov ax,0
...
jmp L1; 不斷地循環



示例二:

1
2
3
4
5




   mov ax,0
mov ecx,5;循環5次
L1:
inc ax
loop L1




label 是僞指令,嵌入源代碼中的命令,由彙編器識別和執行,不在運行時執行

label 0x2:C 語言

label 語法如下:

1
label: statement

在彙編語言中將 label 當作指令的位置標記,C 語言中也是如此,C 語言使用 goto 語句配合跳轉到指定的 label 處,類似彙編的 jmp 效果,goto 語法如下:

1
goto label

示例

1
2
3
4
5
6
7
8







void func() {
    int a;
    a=0;
    loop:
    a++;
    if(a<10) goto loop; // 使用goto實現循環
    printf("%d",a);
}







由於 goto 的自由和靈活,在程序上可隨意跳轉,在使用不當的情況下會破壞“結構化”,不但帶來編程的混亂,而且容易出錯,所以在很多語言教學上都不建議使用。

label 0x3:JavaScript 語言

label 語法如下:

1
label: statement;

語法同 C 語言,但是 JS 沒有 goto 語句,不能像 C、彙編那樣隨意跳轉,JS 的 label 語法只能配合 break、continue 進行使用,單獨使用時無意義。在 JS 裏 continue/break label 應該算是一個閹割版 goto 語句。
配合 continue 使用時,語法如下:

1
continue label;

示例:

1
2
3
4
5
6
7
8
9
10
11










var i, j;
loop1: for (i = 0; i < 3; i++) {
//The first for statement is labeled "loop1"
loop2: for (j = 0; j < 3; j++) {
//The second for statement is labeled "loop2"
if (i === 1 && j === 1) {
continue loop1;
}
console.log("i = " + i + ", j = " + j);
}
}










特別注意:continue label 後的 label 必須緊跟着 for、while 一起時纔可以,否則會報錯,如下:

1
2
3
4
5
6





// × 錯誤
loop1: {
for (;;) continue loop1;
}
// √ 正確
loop1: for (;;) continue loop1;





配合 break 使用時,語法如下:

1
break label;

相比 continue ,break label 的使用靈活會更大,可以和 For、While、Switch、Block 語句配合,如下:

1
label: ForStatement | WhileStatement | SwitchStatement | BlockStatement

示例一:

1
2
3
4
5
6
7
8
9
10
11
12











var i, j;
// 使用for while switch等
loop1: for (i = 0; i < 3; i++) {
//The first for statement is labeled "loop1"
loop2: for (j = 0; j < 3; j++) {
//The second for statement is labeled "loop2"
if (i == 1 && j == 1) {
break loop1;
}
console.log("i = " + i + ", j = " + j);
}
}











示例二:

1
2
3
4
5
6
7
8
9








// label: 語句塊
outer_block: {
inner_block: {
console.log("1");
break outer_block; // breaks out of both inner_block and outer_block
console.log(":-("); // skipped
}
console.log("2"); // skipped
}








單獨使用 label

label 在不配合 continue、break 使用時無意義,解釋器會直接忽略 label,示例:

1
2

test: 1 + 1;
// 2

label 0x4:最後

label 語法在高級語言中的存在感都非常低,主要還是因爲高級語法已經提供了非常多的控制語句,絕大部分場景下已經不需要使用 label、goto 這種相對直接的跳轉。label 的作用本質上還是代碼位置標記,以便類似 goto 的語句進行跳轉,雖然大部分情況都是不建議使用,但有時 goto 可以大幅度簡化代碼量(如:跳出多層嵌套,跳出多層循環場景),在保證代碼足夠清晰明確下,偶爾使用也是可以的。

回到文章開頭,關於 Vue Ref 提案之所以引起許多開發者的注意跟討論,主要還是因爲修改了 label 的語義,儘管符合 JS 語法,但爲 label 增添的非標準語義,會讓部分開發者陷入混亂,增加心智負擔。

討論傳送門:New script setup and ref sugar

JavaScript
感謝您的閱讀,本文由 凹凸實驗室 版權所有。如若轉載,請註明出處:凹凸實驗室( https://aotu.io/notes/2020/11/23/vue-ref-sugar-javascript-label/
上次更新:2021-01-07 10:16:31
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章