iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 12
0
自我挑戰組

你為什麼不問問神奇 JavaScript 呢?系列 第 12

Day12 - 文法

好不容易學會了各種單字
接下來就是把他們組在一起了
在這個單元裡,會先解釋單字和片語,然後再把段落引出來。
了解述句、運算式和區塊。

述句與運算式

述句( statement ) 就是 句子。
運算式( expression ) 就是 片語。

為什麼分辨他們很重要?你需要知道他們回傳給你的是什麼。

var a = 3 * 6;
var b = a;
b;

JS 中的每個運算式都能夠被估算 ( evaluate ) 為單一特定的值作為結果。

  • 3 * 6, a, b 都是運算式。他們都會估算當下儲存的變數值。

  • a = 3 * 6, b = a 是指定運算式( assignment expressions )。

  • 最後一行 b 自成一格,是運算式述句 ( expression statement )。

述句完成值

述句都有完成值。( completion values,即便那個值只是 undefined。)

就是在 Devtool,常看到的灰色 undefine,就是當下述句的完成值。

如何在程式中呼叫呢?
要用到 eval 和 ES7 的 do(..) 解決方案。(請看書)
但是 eval 別使用,ES7 還在規劃。(這就是沒有吧!?)

目前述句完成值還不是很重要,但未來可能會佔有一席之地。

運算式副作用

大多數運算式不會有副作用。

(什麼是副作用? 真的是很疑惑,目前是以治療咳嗽,會產生頭暈的狀態看待。)

  1. 函數的副作用
    可能帶有副作用的運算式,最常見的是函式呼叫運算式 ( function call expression )。
function foo(){
    a = a + 1;
}
var a = 1;
foo(); // 結果:undefined,副作用:a 的值變了。
  1. 其他副作用
var a = 42;
a++; // 42
a;   // 43
var b = 42;
b++; // 43
b;   // 43

都是產生副作用,但產生的時間點不太一樣

  • 副作用在 a++ 運算式回傳值 發生。
  • 副作用在 ++b 運算式回傳值 發生。
    (記法:++b 會先 ++ 再回傳 b )

如何想封住後發的副作用,怎麼辦?(讓兩個值相等)
可以用,述句序列逗號運算子 ( statement-series comma operator )

var a = 42;
(a++, a);   // 43
a;          // 43

運算式 a++,a,代表第二個 a 述句運算式,會在 a++ 運算式的後發副作用之後被估算。
意味著,他會回傳43。

  1. 物件的副作用
    delete,用來移除 object 的一個特性或 array 的一個插槽。
var obj = {
    a: 42
};

obj.a;        // 42
delete obj.a; // true
obj.a;        // undefined

delete 結果:true,副作用:移除 a。
( 內心吶喊,原來 移除 a 是副作用啊!)。

  1. 指定運算子 ( assignment operator ) 的副作用
var a;
a = 42; // 42
a;      // 42

a = 42 看起來不像是有副作用的運算子。
但是我們檢視 a = 42 述句的結果值,就是剛才指定的值 42。
所以將同樣的值,指定給 a 就是副作用。

var a, b, c;
a = b = c = 42;

c = 42 運算式的值 42,副作用是指定了 42 給 c。
這個結果變成看 b = 42,再以此類推,得到 a = 42
在指定的部分,都是副作用。

書上有另一個藉由副作用重構程式碼的方式,請再參考吧!

取決於上下文的規則

相同的文字,可能會因為上下文的關係,而改變他的意思。

JavaScript 也一樣。

相同的符號,會因為上下文,有不同的用途。

1. 大括弧

主要有兩種用途。

  • 物件字面值 ( object literals )
  • 標籤 ( label )

a. 物件字面值

當 {..} 是被 指定某符號 的一個值。就成為物件。

var a = {
    foo: bar()
};

b. 標籤

那沒有 assign 的 {..} 呢?
就是普通的區塊,但是這在 let 區塊範疇宣告會特別有用。

若我的區塊有名字,在程式執行時,就可跳到特定區塊執行。
(有點像 goto,能不用就別用,非常少見)

foo: for (var i=0; i<4; i++){
    for (var j=0; j<4; j++){
        if (j == i){
            continue foo;
        }
        if ((j * i) % 2 == 1){
            continue;
        }
        console.log( i, j );
    }
}
// 0 0 (i==j) continue foo,跳出迴圈,從 foo 再跑 i++
// 1 0 回傳 log
// 1 1 (i==j) continue foo,跳出迴圈,從 foo 再跑 i++
// 2 0 回傳 log
// 2 1 回傳 log
// 2 2 (i==j) continue foo,跳出迴圈,從 foo 再跑 i++
// 3 0 回傳 log
// 3 1 ((j * i) % 2 == 1),continue 重跑回圈 j++>。
// 3 2 回傳 log
// 3 3 (i==j) continue foo,跳出迴圈,從 foo 再跑 i++
// 4   不進入迴圈。

如果你要使用帶標籤的跳躍,請確保你有以大量的註解說明你在做什麼。

c. 區塊

來解說之前提過的問題。

[] + {}; // "[object object]"
{} + []; // 0
  • 因為 + 兩邊會做 ToPrimitive 強制轉型。結果分別是 """[object object]"。這是字串合併的結果。
  • 因為 {} 只是 區塊。整句可以看成 {}; +[];+[] 會將 String 轉成 Number,得到 0

d. 物件解構 (object destructuring)

從 ES6 開始,新增的功能。

function foo({a,b,c}) {
    console.log(a, b, c);
}
foo({
    c: [1, 2, 3],
    a: 42,
    b: "foo"
});         // 42 "foo" [1, 2, 3]

藉由使用具名的函式引數 ( named function argument ),就可以取出物件裡特定的屬性。

e. else if 與選擇性區塊

下面是常見的基本語法

if (a) {
    // ..
}
else if (b) {
    // ..
}
else {
    // ..
}

其實它實際上是這樣剖析

if (a) {
    // ..
}
else {
    if (b) {
        // ..
    }
    else {
    // ..
    }
}

if 與 else 述句接附的區塊若只含單一個述句,就能夠省略他們周圍的 {}

if (a) doSomething(b);
// 等同於
if (a) { doSomething(b); }

但有些風格指南會要求一定要加上 {}。

如果你也慣用 else if 的話,你也就間接打破風格了喔!啾咪。

好的~

今天就是了解

  • 述句和運算式的差異
  • 運算式的副作用 ( 就是述句回傳值以外的動作。)
  • {}

下一章就是

  • 運算子的優先順序
  • 要不要加分號的愛恨情仇
  • argument 的使用
  • 勘誤

明~天~見~

參考資料

  1. 你所不知道的 JS
  2. MDN - 逗號運算子 ( comma operator )

上一篇
Day11 - 寬鬆相等 VS. 嚴格相等
下一篇
Day13 - 運算子優先序
系列文
你為什麼不問問神奇 JavaScript 呢?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言