iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 21
0
Modern Web

跟著 YDKJS 作者 Kyle Simpson 打造全新 JavaScript Mindset系列 第 21

[day20] YDKJS (Scope) : Advanced Scope

  • 分享至 

  • xImage
  •  
  1. Lexical & dynamic scope
    • lexical scope
    • dynamic scope
  2. You-Dont-Know-JS/scope & closures/ch3.md
    • function scoping (You-Dont-Know-JS/scope & closures/ch3.md)
    • IIFE pattern (You-Dont-Know-JS/scope & closures/ch3.md)
    • block scoping
  3. choosing let or var
  4. explicit let block
  5. const
  6. 總結

名詞辯證

  1. Lexical Scope
    講了那麼多,其實就是會 nested 的 scope , 通常在 compiler 就 parser 完成。

    所以想到 Lexical Scope ,就要想到第一階段的 compiler time 。我們小球和木桶的對話。

  2. dynamic scope
    常見聽到的是 Bash ,也是最多人誤解 JavaScript 的環節。
    像是 Bash script,並沒有 compiler 的階段,所以無法產生 Lexical Scope,就只是一行執行一行,也就是 rum time 執行。

Lexical Scope : 可以預測的 Scope

很像的這張圖,現在應該很清楚明白,我們是 compiler time 先決定好 Scope 了,所以不用等到rum time,我們知道 teacher 是藍色小球的 Scope。

希望看到 Lexical Scope ,大家就想到 compiler time,也想到我們的這張圖:

如果你用 sublime,你可以找這個套件 (ES levels)
https://packagecontrol.io/packages/Levels

會有點像這樣:

line 7 的 teacher 會是和上層 function 一樣顏色。

*註:這個套件對 named function expression 有小小 bug, 都會是 global ,
因為要處理這個部分,會產生太多顏色,避免專案太過麻煩,所以這個 bug 沒有修。

Dynamic scope : 很彈性,隨時會改變的 Scope

JavaScript 不是 Dynamic scope。

如果 JavaScript 是 Dynamic scope ,你會看到以下錯誤:

執行到 ask() , 但不知道 teacher 在哪邊,或是說, ask() 放在每個不同的位置,每次都會產生不同結果,因為 runtime 才決定要顯示什麼值。

function scoping

var teacher = "Kyle";

// do somethings 

console.log(teacher);  // Kyle

以上code 沒有太多問題,唯一的問題,可能是 do somethings

比如有人在半年後改成這樣:

var teacher = "Kyle";

// 6 months now.
// 我有新的需求,來改點 code ...
var teacher = "Suzy";
console.log(teacher); // Suzy
// ...
// end

console.log(teacher);  // Suzy -- OOPS!!

你的 code 爆炸了,可能是有人沒看到,沒注意變數被用過,反正你的程式壞了。
你可能覺得 「那我用 const 啊!」
那我還是可以把所有 const 改成 var ...

真正問題不是 變數被重新賦值
問題是 變數命名重複問題 (naming collision problem)

最好的解決方法,就是把不同變數放在不同桶子(scope)
也就是我們一直出現的範例:

這邊的 var teacher 就不會被改寫了,大家的區域分明。
不過還是一樣都是 var teacher ,沒有完全解決 變數命名重複問題 (naming collision problem)

如果你讀資工或相關書籍,會有讀過 最小權限原則(Principle of least privilege)
你應該預設把值隱藏起來(keeping everying private),只把最少量真正需要的訊息公開。

如果你按照最小權限原則,你可以處理掉三個問題:

  1. 變數命名重複問題 (naming collision problem)。
  2. 別人不會意外改到你的 code,也不能隨意存取你寫好的東西。
  3. Kyle 認為最重要的: 你可以保護你未來 重構(refactoring) , code 不會因為被其他人使用而不能改動。

如何做到? 看看 line 8

把一個變數,當成 expression 。

IIFE pattern 立即執行函式


如圖,取代掉原本的變數。

Immediately invoked function expression

  1. 只會,也只需要執行一次
  2. 被呼叫立刻執行
  3. 不汙染外圍 Scope

Q: 為什麼不是 function declaration?
A: 因為不是 function 當做第一個字開頭

這樣 JavaScript 才可以判斷是 function expression(IIFE) 還是 function declaration

順帶一提,你常看到的模式應該長這樣,

但是 Kyle Simpson 史上超級無敵討厭 匿名函式(Anonymous function),
所以他的範例都會給名字,就算沒有什麼特別的命名,他至少還是會給IIFE 這個字當作 fn 名字 XDD

看到 IIFE 總比看到 anonymous function 心情還要好。

當然,IIFE 也是可以傳參數:

特殊用法

IIFE 特性是把 statement 轉換成 function expression 來執行。

如果你要用 error handler 處理 assignment,並不會被捕捉到,
所以轉成 IIFE 用 expression 來執行,就可以被捕捉到了。

範例是先宣告在 line 1, 後面 line 3, line 6 就是 expression

如果你用 IIFE ,可以不用寫多餘的 teacher 變數。

這個例子不常見,只是說明你可以善用 IIFE 的特性。

block scoping 就是 {..}

比較 IIFE 和 block 的寫法:


這邊你應該注意到,不能用 var
也是寫到快結束,才真正開始出現 let

  • let 和 block({..}) 一起出現: block 內部轉換(implicit)成有 scope

let 這邊改成 const 一樣效果。

  • var 和 block({..}) 一起出現: block 內部不會有 scope

你使用 block scoping 要表現強烈意圖: let, const 是 block scope declaration

你原本寫可能是這樣:

但是如果你想強調 Scope , 使用 let :

choosing let or var

Kyle Simpson 不使用 let, const 替換所有 var ,因為他的 let, const 有語意(block scope)

TC 39 成員寫:「let is new var」,Kyle 抱持反對意見

氣死,超好笑

  • 不要混淆語意

比如, for loop 裡面 應該使用 let

Kyle :「你應該學會你的工具,然後好好使用。」

順帶一提,你用 ESlint 會提示不要用 var , Kyle 關掉那個提示了 XDDD

  • 其他例子:

    這個例子,你只能使用 var,
    因為 let + {..} ==> Scope 。

*註:這邊的 code 也只是舉例,不是真實世界常見的 code。
但是 Kyle 如果在寫長 funtion ,他總是重複宣告 var

  1. 他知道重複宣告不會影響 scope, 但讀 code 的人不用找個 200 行只為了看一行宣告。
  2. var 關鍵字有宣告的語意,天生可以重複使用。
  3. 這件事 ESlint 會警告,也不能用 let ,所以他關掉 ESlint 提示。
  4. 如果 let,const 可以完全取代 var,那應該會廢棄 var ,但沒有。表示你應該謹慎思考兩者的差別。

explicit let block

使用 let 的建議: 你應該總是記得有 block {..}

否則,你使用的 let 可能沒有語意。

Kyle 會故意把 let 的宣告和 { 放在同一行,來表示 let block scope。

const

const 不是 JavaScript 限定,但是其語法很容易造成寫過其他程式語言的誤解。

  • 一般來說,看到 const 會想 constant --> 不會被改變 // (這是錯的)
  • 對程式來說, const 代表 不能被重新賦值 (reassigned)

line 8 : 我們沒有 reassign teachers ,我們的對象是 teachers[1]

*註:Java 把 const keyword 改成 final ,希望減少誤解 。

這邊 Kyle 舉一個很有趣的例子:
const teachers = ["Kyle", "Suzy"];

我們現在都知道,其實有各種方法可以改掉 teachers 這個 array。
但是慣例上 大家還是用 const 。

就像是小孩子害怕衣櫃裡面有怪物,所以我們幫小朋友留一盞夜燈。
但是,我們都知道,衣櫃裡面不可能有怪物

  • Kyle 只對 primitive value(string, boolean, number) 使用 const keyword.

總結:

Kyle 自己的意見:

  1. default 使用 var 當作宣告。
  2. 使用 let 在需要 block scope 的時候。
  3. const 只對 immutable primitive value(string, boolean, number)。

比如數學的常數 , 比如 API 的 base URL

註:明天終於來到 hoisting !!


上一篇
[day19] YDKJS (Scope) : Kyle Simpson 史上超級無敵討厭 匿名函式(Anonymous function)
下一篇
[day21] YDKJS (Scope) : Hoisting ? let 會 Hoist 嗎 ?
系列文
跟著 YDKJS 作者 Kyle Simpson 打造全新 JavaScript Mindset31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言