[ Javascript ] Javascript的奇怪部分 (40%)
js 是在 execute context (執行環境) 中實際被轉成電腦看得懂的東西
在 execute context 中分成兩個階段
Synchronous -> "照順序"一次執行一行程式碼
* Javascript在執行的時候誰會最先被創造出來?
-----> Global Object & this
函數呼叫
Execution Stack
arguments 包含了所有你傳入 function 內的值
spread (...) 在 function 內剩下的參數, 都可以被放到 ...後面接的變數名稱內
在 execute context 中分成兩個階段
- 創造階段 Creation Phase
- 建立全域物件 (Global Object) 跟 this, 如果有連結到外部, 也會產生外部環境的連結
- 將宣告的變數 (variables) 跟 functions 先在記憶體中佔一個位子, 但變數尚未給值, 而 functions使用 function b () {} 這種方式的會有值
- 因為有用等號給值的只會等到真正執行的時候, 才會知道當下執行的值會是什麼
- 所以當只有在記憶體內先佔一個位子時, 所有的變數值都會先被給定 undefined
- 上面這點就是一般說的 Hoisting
- 執行階段 Execution Phase
- 逐行編譯與執行你的程式碼
Synchronous -> "照順序"一次執行一行程式碼
* Javascript在執行的時候誰會最先被創造出來?
-----> Global Object & this
函數呼叫
- 當一個函數被呼叫的時候, 函數內部會在自己產生一個 Execution context (執行環境)
- 放入 Execution Stack
Execution Stack
- 在Stack最上方的就是先執行的
- 以上圖來看, 第一步驟是先透過 compiler/ interpreter 去翻譯 js code, 在 global execution context中, 先產生 global object 與 this
- 接下來將 function b & function c 放入記憶體中並給定完值
- 執行到 a() 時, 先將 function a 的 execution context 產生出來並放入 execution stack的最上方
- 在 a() 的 execution context (執行環境) 中, 執行到 b()
- 然後暫停目前執行的工作
- 產生 b() 的 execution context (執行環境), 並放入 execution stack 的最上方
- b() 沒做啥事情, 跑完後將 stack 中的 b() pop 出去
- 回到 a(), a() 中執行完 b() 後也沒其他事情, 將 stack 中 a() pop 出去
- 回到 Global Execution context
變數環境
- 描述變數的生命週期, 還有在記憶體中, 變數們彼此的關聯
- 可以單純想像成 Where is the variable ?
- 程式碼所寫在的位置
- undefined
- null
- boolean
- String
- Number
- 只有浮點數
- 可以假裝是整數
- symbol
- es6
- 是一個內建寫好的 infix function (在中間)
- prefix +12
- suffix 12+
- infix 1+2
- 當 operator 的 precedence 都一樣的時候, 就是看 associativity 來決定由左到右來執行, 還是由右到左
- Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
Comparison Operator
- console.log(3 < 2 < 1) -> true
- precedence 相同, 所以看 associativity -> left to right
- 3 < 2 --> false
- false < 1
- 強制轉型
- 0 < 1 --> true
- console.log(1 < 2 < 3) -> true
- 1 < 2 --> true
- true < 3
- 強制轉型
- 1 < 3 --> true
- console.log(3 == '3') -> true
- 強制轉型
- left to right
- 3 == 3 --> true
- undefined & null 的強制轉型可能會跟預期中的不一樣!
- 99%的情況下請使用 === , 而非 == , 除非你有特定目的, 否則不推薦
- === / Object.is(a, b)
- +0 === -0 --> true
- Object.is(+0, -0) --> false
- 不存在的東西透過 Boolean 強制轉型都會轉成 false
- Boolean(undefined) -> false
- Boolean(null) -> false
- Boolean("") -> false
- 在 if () 內的 statement 都會被嘗試轉為 Boolean
- var a = 0
- if (a) { console.log('a exists') } 這句會失敗
- 因為 Boolean(0)會被轉成 false
- 如果a有機會變成0的話, 正確寫法 if (a || a=== 0)
- undefined || 'hello'
- 回傳可以產生 true 的值
- 因為 Boolean(undefined) -> false
- Boolean('hello') -> true
- 所以回傳 true 的那一個
- "" || 'hello' -> 'hello'
- 0 || 1 -> 1
- null || 'hello'
- Object 0x001
- reference 0x002 primitive property (number, boolean, .. etc)
- reference 0x003 Object property
- reference 0x004 methods (function inside a object called methods)
- 建立物件的時候, 建議使用 object literal (物件實體), 不要用 new Object()
- Namespace 命名空間
- 可以想像成是維持變數與 functions 不被其他人感染的容器
- 你可以對其他types做的事情, 一樣可以對 function 做
- 把 function 指派給一個變數
- 把 function 當成參數傳來傳去..等等
- function 就是物件
expression
- a unit of code that results in a value
- 產生一個值, 不必存在變數內, 必定會回傳一個值
- a = 3 ---> return 3;
- = 是 運算元, 會回傳值
- 1 + 2 ----> return 3;
- + 是運算元, 會回傳值
- 1 === 1
- === 是運算元, 會回傳值
- 不會回傳值的陳述句
- if () { ... }
by value
- a = 3, b= a
- 純值的 assign 會是透過 copy 那個純值, 然後再 assign 給 b
- a=Object, b=a
- 物件的話, 是把b也指向原本a指向的記憶體位置
上圖例, 因為 = 這個運算元發現, 要設定新的物件給 c, 所以會把 c 指向新的記憶體位置
所以輸出的c, d分別為 { greeting: 'howdy' }, { greeting: 'Hola' }
!! 注意: 因為 { greeting: 'howdy' } 不是已存在的物件, 所以要分配新的記憶體位置 !!
!! 注意: 因為 { greeting: 'howdy' } 不是已存在的物件, 所以要分配新的記憶體位置 !!
setname 的 this 會是指向到 global object, 也就是 window, 即便 setname function 是在 c 的 log function 內!
arguments 包含了所有你傳入 function 內的值
spread (...) 在 function 內剩下的參數, 都可以被放到 ...後面接的變數名稱內
- function greet(a, b, c, ...other) { .. }
- greet('Neil', 'Wang', 'En', 'address')
- other => ['address']
留言
張貼留言