[ Javascript ] Javascript的奇怪部分 (40%)

js 是在 execute context (執行環境) 中實際被轉成電腦看得懂的東西
在 execute context 中分成兩個階段

  1. 創造階段 Creation Phase
    • 建立全域物件 (Global Object) 跟 this, 如果有連結到外部, 也會產生外部環境的連結
    • 將宣告的變數 (variables) 跟 functions 先在記憶體中佔一個位子, 但變數尚未給值, 而 functions使用 function b () {} 這種方式的會有值
    • 因為有用等號給值的只會等到真正執行的時候, 才會知道當下執行的值會是什麼
    • 所以當只有在記憶體內先佔一個位子時, 所有的變數值都會先被給定 undefined
    • 上面這點就是一般說的 Hoisting
  2. 執行階段 Execution Phase
    • 逐行編譯與執行你的程式碼
Single Thread -> 一次執行一個指令
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 ?
Lexical Environment
  • 程式碼所寫在的位置
Primitive Type (非物件的單值)
  • undefined
  • null
  • boolean
  • String
  • Number
    • 只有浮點數
    • 可以假裝是整數
  • symbol
    • es6
Operator
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
Existence & Boolean
  • 不存在的東西透過 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 & functions
  • 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 不被其他人感染的容器
First Class Function
  • 你可以對其他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
      • === 是運算元, 會回傳值
statement
  • 不會回傳值的陳述句
    • if () { ... }
by value

  • a = 3, b= a
    • 純值的 assign 會是透過 copy 那個純值, 然後再 assign 給 b 
by reference

  • a=Object, b=a
    • 物件的話, 是把b也指向原本a指向的記憶體位置
Object就算透過參數的方式傳到 function 內, 也仍然是傳 reference, 所以 function 內如果有改變傳入參數 Object 的值, 原本的 Object 值也會跟著改變
上圖例, 因為 = 這個運算元發現, 要設定新的物件給 c, 所以會把 c 指向新的記憶體位置
所以輸出的c, d分別為 { greeting: 'howdy' }, { greeting: 'Hola' }
!! 注意: 因為 { 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']












留言

這個網誌中的熱門文章

[MySQL] schema 與資料類型優化

[翻譯] 介紹現代網路負載平衡與代理伺服器