--- title: 'JS 核心 16 - 函式參數' tags: JS 核心 ,JS , JavaScript, 函式參數 description: 2021/02/13 --- JS 核心 -- 函式參數 === ## 函式在運行時會帶入的變數 ``` var globalVariable = '全域變數'; // 定義了全域變數 var obj = { // 在此物件下,定義了另一段函式 aFunction: function(para) { // 帶入三個參數,只取其中一個參數 var localVariables = '區域變數'; // 定義的區域變數  console.log(para, localVariables, arguments, this, globalVariable); } } obj.aFunction('一段描述', 2, 3); // 執行物件內的函式,同時也有帶參數進來 ``` * para : 傳入的參數。外層傳入三個參數,但函式內層只會接收第一個參數。 * localVariables : 在 aFunction 環境下定義的區域變數。 * arguments : 是一個類陣列。無論傳入多少參數,都可全部接收。 * this : 執行函式時會自動帶入的變數。執行物件內函式的 this 會與一般函式有所不同。 * globalVariable : 全域變數。 * 並非來自於函式本身,是來自於外層的變數。 * 在函式內無法取得此變數,所以函式會向外層尋找,並找到此變數為止。 * 可參考[範圍鏈章節](https://hackmd.io/U0vADI2zR76UYnGXMaklAQ)。 ![](https://i.imgur.com/mlrtX0g.png) ### :pencil2: **範例** ``` function callName(a) { // 在傳入參數時已定義好值了,變數 a 已存在 console.log(a); // 小明 var a; // 宣告一個變數時,若此變數已經存在,則重複宣告是無作用的 console.log(a); // 小明 a = '杰倫'; // 若要改變 a 的值,要使用重新賦予值的方式覆蓋掉 console.log(a); // 杰倫 } callName('小明') ``` ## 函式的陳述式 在執行 hositing 時,會把整個代碼移到最前方 ``` function callName(a) { // 在傳入參數時已定義好值了,變數 a 已存在 console.log(a); // 小明 function a() {} // 定義一個函式陳述式 var a; // 宣告一個變數時,若此變數已經存在,則重複宣告是無作用的 console.log(a); // 小明 a = '杰倫'; // 若要改變 a 的值,要使用重新賦予值的方式覆蓋掉 console.log(a); // 杰倫 } callName('小明') ``` 執行結果為變數 a 被函式陳述式所取代了。 函式陳述式 function a( ){ } 移到 console.log(a); 之前,但不會比參數更前方。 > <span class="red">**變數 a 在參數一傳入時就已定義好了,hositing 不會影響變數 a 的值。**</span> ``` function callName(a) { // 在傳入參數時已定義好值了,變數 a 已存在 function a(){}; // 函式陳述式 hositing 移到前方,變數 a 被函式陳述式取代 console.log(a); // ƒ a(){} var a; console.log(a); // ƒ a(){} a = '杰倫'; console.log(a); // 杰倫 } callName('小明') ``` ## 參數名稱可以自己定義 1. <span class="red">**參數名稱和傳入的值沒有關聯性**</span> 2. 只重視參數有幾個值、順序是怎樣...,不會受到外層的名稱所影響 3. 若傳入的參數數量不足,會在後方補上 undefined ( 如同變數未給值,也是呈現 undefined ) ``` function callMore(d, c, b, a) { console.log(d, c, b, a); } var a = 'a'; var b = 'b'; var c = 'c'; callMore(a, b, c); // a b c undefined ``` ## 若參數是物件,有傳參考的特性 > 物件透過參數傳遞,也會維持傳參考的特性 > 很多程式碼規範都會建議不要去調整裡面的屬性參數,會導致原有的物件屬性被作調整 ``` function callObject(obj) { obj.name = '杰倫家';  // 修改物件內的屬性值 } var family = { name: '小明家' } callObject(family); // 執行 callObject 函式時,把物件傳進去 console.log(family); // {name: '杰倫家'} ``` ## 回呼函式 Callback Function * 把函式當作另一個函式的參數,透過另一個函式來呼叫它 * 這個函式只會在滿足了某個條件才會被動的去執行 * 定義一段函式,並把函式傳進來 ### 函式直接寫在參數內 > fn 是在 functinoB 內建立的一個參數,在這邊是要說明函式傳入的參數也可以是一個 function,並且在函式中呼叫傳入函式,而這也就是所謂的 callback function。 1. functinoB 執行時將函式作為參數傳入 fn,並在 functinoB 內執行。 2. fn 的小明做為參數傳遞給 functinoB 執行時的函式。 ``` function functionB(fn) { // 直接意義一段函式,並透過參數把函式傳進來 fn('小明');        // 裡面執行的此段函式,會把參數傳遞到外層來 } functionB(function(a){console.log(a)}) // 小明 // 執行這段函式時就可帶入另一段函式 (可以是匿名的) ``` ### 預先把函式定義好 1. 直接把 callSomeone 函式帶入,等同說執行另外一段表達式。 2. 把 callSomeone 函式賦予到 fn 變數上,所以 callSomeone 函式不用帶參數。 3. 首先在這邊函式 callSomeone 被當作參數傳入到 functionB,所以 functionB 的 fn 其實就是 callSomeone 的參數。 4. 直接在 functionB 中使用 fn ( ) 呼叫並傳入參數執行 functionB。 ``` function callSomeone(name, a){ // 預先定義好另一段函式 console.log(name + '你好', a) } function functionB(fn) { // 直接定義一段函式,並透過參數把函式傳進來 fn('小明', 1); // 實際執行的是此段函式,會把參數回傳到外層 } functionB(callSomeone); // 小明你好 1 // 呼叫 functionB,然後傳入的參數是一個 function,也就是 callSomeone // 注意傳入的函式並沒有執行 // 傳入 functionB 的是一個完整的函式 ``` ## arguments 變數 * arguments 是專有名稱,不需另外定義,執行函式時就會自動帶上 * arguments 為類陣列 (物件),可以做基本的陣列操作 * 可使用 for 迴圈取值。 * 沒有大多數陣列可使用的方法,會報錯。 * arguments 可自動帶上傳入所有參數的值 ### arguments 類陣列可使用 for 迴圈取值。 ``` function callArg(a) { console.log(a, arguments); // 1 Arguments(4) [1, 2, 3, "4", callee: ƒ, Symbol(Symbol.iterator): ƒ] for (let index = 0; index < arguments.length; index++) { // 試著使用 for 迴圈把值取出 console.log(arguments[index]); // 1, 2, 3, '4' } } callArg(1, 2, 3, '4') ``` ### arguments 為類陣列不是純陣列,沒有大多數陣列可使用的方法,會報錯。 ``` function callArg(a) { console.log(a, arguments); arguments.forEach(function() { // 無法使用陣列方法 }) } callArg(1, 2, 3, '4') // 跳錯,arguments.forEach is not a function ``` ## :memo: 學習回顧 :::info * 函式在運行時會帶入的變數 * para : 傳入的參數。若傳入參數很多,函式內層只會接收第一個參數。 * localVariables : 在函式環境內定義的區域變數。 * arguments : 是一個類陣列。無論傳入多少參數,都可全部接收。 * this : 執行函式時會自動帶入的變數。執行物件內函式的 this 會與一般函式有所不同。 * globalVariable : 全域變數。 * 並非來自於函式本身,是來自於外層的變數。 * 範圍鏈 : 在函式內無法取得此變數,所以函式會向外層尋找,並找到此變數為止。 * 「函式的陳述式」在執行 hositing 時,會把整個代碼移到最前方,但不會比參數更前方。 * 變數 a 在參數一傳入時就已定義好了,hositing 不會影響變數 a 的值。 * 參數名稱可以自己定義 * 參數名稱和傳入的值沒有關聯性 * 只重視參數有幾個值、順序是怎樣…,不會受到外層的名稱所影響 * 若傳入的參數數量不足,會在後方補上 undefined * 若參數是物件,有傳參考的特性 * 建議不要去調整裡面的屬性參數,會導致原有的物件屬性被調整 * arguments 變數 * arguments 是專有名稱,不需另外定義,執行函式時就會自動帶上 * arguments 為類陣列 (物件),可以做基本的陣列操作 * 可使用 for 迴圈取值。 * 沒有大多數陣列可使用的方法,會報錯。 * arguments 可自動帶上傳入所有參數的值 ::: ## :+1: 相關參考文件 :::info [JavaScript 什麼是Callback函式 (Callback Function)?](https://matthung0807.blogspot.com/2019/05/javascript-callback-callback-function.html) [你懂 JavaScript 嗎?#23 Callback](https://cythilya.github.io/2018/10/30/callback/) ::: <style> .red { color: red; } .green { color: green; } </style>