# 讀書會 01 ###### tags: `讀書會` > 2021/02/01 筆記 ### 全域環境 & 全域變數 #### **1.執行 JavaScript 程式** JavaScript 檔案被載入 > 語法解析啟動 > 執行環境創造(基礎執行環境) > 創造「全域物件」、「 this」 ##### (執行環境可以決定現在這個 this 的值是什麼) #### **2.何謂全域變數 / 區域變數** ``` var name = 'Dora'; ---> 全域變數 var job = 'Engineer'; ---> 全域變數 function a (){ console.log(name) var job = 'guide'; b(); } function b(){ var year = 18; ---> 區域變數 console.log(job); ---> 該「執行環境」中沒也'job'變數時,將往「外部環境」去找 } a(); ---> 當函式被呼叫會創造新的執行環境 result: // Dora // Engineer ``` #### **3.var / let 差異*** let / var 變數都會被創造在執行階段、被放入記憶體中但值為「undefined」 和 var 較為不同的是,直到執行階段 let 被執行、宣告變數後,才能存取用「let」宣告的值。 ![](https://i.imgur.com/jqIpcIm.png) #### **4.關於 this** 環境創造(基礎執行環境) this 指向的是同一個全域物件 window this 的值跟作用域跟程式碼的位置在哪裡完全無關,只跟「你如何呼叫」有關 --- ### call、apply、bind call、apply、bind 三者都是 JavaScript Function 的內建函式,他們與 this 的關係重大,除此之外,call & apply 可以作為呼叫 Function 的另一個手段,而 bind 則會回傳一個經過包裹後的 Function 回來。 #### **1.call** call 的使用方法是 fn.call(this, arg1, arg2…, argn)。call主要有兩種參數: 第一個參數: 輸入的物件會被指定為目標函式中的 this 第二以後的參數: 會作為參數傳進目標函式中,如果目標函式中不需要參數則不要傳入即可 ``` 範例: function add(a, b) { return a + b; } console.log(add(1, 2)); // 3 console.log(add.call(null, 1, 2)); // 3 ``` #### **2.apply** call 跟 apply 執行後的功能一模一樣,但因為參數不同 apply 是陣列 ; apply 的使用方法是 fn.apply(this, [arg1, arg2…, argn])。apply主要有兩個參數: 第一個參數: 輸入的物件會被指定為目標函式中的 this 第二個參數: 必須是陣列,會把陣列中的每個元素作為參數傳進目標函式中,如果目標函式中不需要參數則不要傳入即可 讓我們用跟 call 一樣的範例來做個比較: ``` 範例 function add(a, b) { return a + b; } console.log(add(1, 2)); // 3 console.log(add.call(null, 1, 2)); // 3 console.log(add.apply(null, [1, 2])); // 3 ``` 第二個參數必須是陣列如果未遵循將出現以下 error ``` console.log(add.apply(null, 1, 2)); Uncaught TypeError: CreateListFromArrayLike called on non-object。 ``` #### **3.bind** 明確指定 this 回傳一個包裹函式,當我們執行這個函式時,同時也會將帶入 bind 的 arguments 一起帶進 Function 中。類似 Currying 的概念。bind 的使用方法是 fn.bind(this, arg1, arg2…, argn)。bind主要有兩種參數: 第一個參數: 輸入的物件會被指定為目標函式中的 this ( 以硬繫結的方式 ) 第二以後的參數: 會作為往後傳進目標函式的參數,如果目標函式中不需要參數則不要傳入即可 回傳: 回傳包裹後的目標函式。執行這個包裹函式後,可以幾乎確定 this 不會被改變,另外,也可以把先前傳入 bind 的參數 一並帶進目標函式中 --- ### JavaScript 閉包 閉包的概念:一個函式可以操作另一個函式作用範圍內的參數只有巢狀結構的函示才會形成閉包 只能內(內層函式)存取外(外層函式);不能外存取內 閉包範例: ``` function A(){ var z = 0 function B(){ console.log(z) // 輸出 0 } B(); } A() 如果要把B()留存的話並存取A的宣告需要在外宣告一個變數G並且指向B形成一個閉包可以一直使用到A()裡的變數,更改如下 var G; function A(){ var z = 0 function B(){ console.log(z) // 輸出 0 } G = B; } A() G() ``` 非閉包範例: ``` function B(){ console.log(z) //因為非閉包巢狀結構所以拿不到上層的宣告 } function A(){ var z = 0; B() } ``` 其他閉包應用: ``` var name = "李大頭" var obj = { name:"史努比", getName:function(){ return function(){ console.log('哈囉'+this.name) return this.name } } } var result = obj.getName() console.log(result()) //輸出 李大頭 (因為是在全域環境下呼叫result()所以其中getName()的this會指向windows 就會抓到第一個全域變數name) ``` 解法一: 重新宣告一個變數指向this ``` var name = "李大頭" var obj = { name:"史努比", getName:function(){ var _ = this return function(){ console.log('哈囉'+this.name) return _.name } } } var result = obj.getName() console.log(result()) 輸出 史努比 ``` 解法二: 使用.bind() 或是.call() ``` var name = "李大頭" var obj = { name:"史努比", getName:function(){ return function(){ console.log('哈囉'+this.name) return this.name }.bind(this) } } var result = obj.getName() console.log(result()) 輸出 史努比 ```