---
# System prepended metadata

title: 十二、 宣告各種函式
tags: [2020七月第二週, JS101, Lidemy心得, 7/8, 進度筆記, JavaScript]

---

# 十二、 宣告各種函式 
###### tags: `JavaScript` `JS101` `2020七月第二週` `進度筆記` `Lidemy心得` `7/8`

---

## function 起手式
 一般宣告函式的方式:

    function hello() {
      console.log('hello')
    } 
      return {
    }
    
函式是 primitive data value ， 因此我們可以:


    var hello = function() {
      console.log('hello')
    }
    
會跟上個式子很相似。

---

而如果:

    function print(anything) {
      console.log(anything)
    }
    
    print(123)
    // 會印出 123 。
    
傳 `print(123)` 參數到 console.log 就會印出 123。
接著:

    function print(anything) { //這邊的 anything 有可能是個 function ;
      console.log(anything)
    }
    
    function hello() { // 假設新增了一個 function 叫作 hello ; 
    
    }
    
    print(hello)
    // 會印出 Function: hello 。
    
可以把最後一行 hello 往上傳進去，再將 function 當作參數傳進去。    
然後:

    function print(anything) { // function print(anything) { 是物件，這邊的 anything 有可能是個 function ;
      console.log(anything)  // 這裡可以傳一個 function 進來，相等於下面 "會變成:" 式子的 anything() ;
    }
    
    function hello() { // function 前面有個 log: ; 等同這行 function 不會被執行，只是被 log ;
      console.log('hello') // 'hello' 是第一行的物件 function print(anything) ， 當這個函式是物件裡面的 method 時，這時候的 this 就會指稱到包含這個 method 的物件;
    
    print(hello)
    // 會印出 Function: hello 。
    
會變成:


    function print(anything) {  // 讓 anything() 變成 function hello() ;
      anything()    // 成為 function 執行他。
    }
    
    function hello() { 
      console.log('hello')
    }
    
    print(hello) // 把 hello 往上傳進去
    // 會印出 hello 。
    
必須在 函式(參數) {區塊內才能放另外一個函式} ，其實為:
最後一行的 `print(hello)` 將 hello 參數設為 function 傳進去; 而第一行 `function print(anything) {` 的 anything 也就是一個參數，也是一個 function ， 相等於第五行的 `function hello()` ，因此如果將最後一行 `print(hello)` 改成 `print(123)` 就會出錯。

示範:

    function transform(x, transformFunction) {
      return transformFunction(x)  // 這一行意思是執行 function test(x)
    }
    
    function test(x) {    // x 接受一個參數
      return x*2 // 回傳 x*2
    }
    
    console.log(
      transform(10, test)  // 這邊的 (10, test) 即第五行的 function test(x) { ;
    )
    // 會印出 20  。
    
    
由於最後一行 `transform(10, test)` 傳到第一行的 x 相等於 10 ， 然後最後一行的 test 被傳到第五行的 function ; 而第一行 transformFunction 相當於參數，也相當第五行的 function ， 然後執行 x*2 。

再換一個方式:
`transform([1, 2, 3], double)` 將任何東西都 *2 => [2, 4, 6]。

    function transform(arr, transformFunction) { // 因最結尾的參數也是函式 transformFunction 被傳過來，這會對 arr 的每個元素都 *2 ;
      var result = []
      for(var i=0; i<arr.length; i++) { // 因結尾的參數被放到第一行，所以迴圈內的值
        result.push(transformFunction(arr[i])) // transformFunction 會建立一個 function 把 (arr[i]) 丟進去，再把他 push 到結果內; 
      }
      return result  // 回傳 result 回去;
    }
    
    function double(x) {    // x 接受一個參數
      return x*2 // 回傳 x*2 ; 如果改成 *3 就會輸出陣列會變成 [3, 6, 9] ;
    }
    
    console.log(
      transform([1, 2, 3], double)  // 這邊的 ([1, 2, 3], double) 會回傳一個參數也是函式 即 function double(x) ;
    )
    
`transform([1, 2, 3], double)` 就會被放到第一行的 `function transform(arr, transformFunction) {`
這是 JS 的重要基礎步驟，結尾的 `console.log()` 不只能回傳數字、字串，還能回傳參數和函式。

## 宣告一個變數等於一個函式:
   
    function transform(arr, transformFunction) { // 因最結尾的參數也是函式 transformFunction 被傳過來，這會對 arr 的每個元素都 *3 ;
      var result = []
      for(var i=0; i<arr.length; i++) { // 因結尾的參數被放到第一行，所以迴圈內的值
        result.push(transformFunction(arr[i])) // transformFunction 會建立一個 function 把 (arr[i]) 丟進去，再把他 push 到結果內; 
      }
      return result  // 回傳 result 回去;
    }
    
    var triple = function() {     // 後面這個 function 是沒有名字的，因為它被賦予了變數 triple ，由變數決定;
    return x*3
    }
    
    function triple(x) {    // x 接受一個參數
      return x*3 // 回傳 x*2 ; 如果改成 *3 就會輸出陣列會變成 [3, 6, 9] ;
    }
    
    console.log(
      transform([1, 2, 3], triple)  // 這邊的 ([1, 2, 3], double) 會回傳一個參數也是函式 即 function double(x) ;
    )   
     /// 這印不出來，只是示範將變數後的參數位置移開;
    /// 可以將 var triple = function() 移到 transform([1, 2, 3], triple) 取代 triple 的位置:
    
    function transform(arr, transformFunction) { // 因最結尾的參數也是函式 transformFunction 被傳過來，這會對 arr 的每個元素都 *3 ;
      var result = []
      for(var i=0; i<arr.length; i++) { // 因結尾的參數被放到第一行，所以迴圈內的值
        result.push(transformFunction(arr[i])) // transformFunction 會建立一個 function 把 (arr[i]) 丟進去，再把他 push 到結果內; 
      }
      return result  // 回傳 result 回去;
    }
   
    function triple(x) {    // x 接受一個參數
      return x*3 // 回傳 x*2 ; 如果改成 *3 就會輸出陣列會變成 [3, 6, 9] ;
    }
    
    console.log(
      transform([1, 2, 3], function(x) {     // 後面這個 function 是沒有名字的，因為它被賦予了變數 triple ，由變數決定;
      return x*3
    }) // 這邊的 ([1, 2, 3], double) 會回傳一個參數也是函式 即 function double(x) ;
    )   
    
    // 會印出 [3, 6, 9] 。
    
將變數等於一個函式，並將它函式的名稱隱藏起來的作法即 "匿名函式 (anonymous function) ， 好處是可以直接改最後 return 的值。

[1. 匿名函式](https://pjchender.blogspot.com/2016/03/javascriptfunction-statements-and.html)

[2. 匿名函式和 this](https://pjchender.blogspot.com/2016/03/javascriptthisbug.html)

---

# 十三、參數和引數

## 常常名詞混用的兩種

作 function 的時候常常會不小心誤會 "參數(parameter)" 和 "引數(argument)" ， 如 `function transform(arr, transformFunction)` 參數其實是括號內的 arr 和 transformFunction ， 即他在 function 長甚麼樣子。

而引數是真正傳進去的東西，如:

    function add(a, b) {    // function add(a, b) 括號內的為參數;
      return a+b
    
    }
    
    add(3, 5)    // 真正傳進去的值為引數;
    
宣告後的引數:    

    function add(a, b) {    // function add(a, b) 括號內的為參數;
      return a+b
    
    }
    
    var c = 10
    var d = 20
    add(c, d)    // 真正傳進去的值為引數;
    
在 call function 的為引數，會包含所有你放入function中的參數值。
但參數和引數其實都是傳進去的值。

---

## 背後的影武者: arguments

JS 在每個 function 後加入了 arguments:

    function add(a, b) {    
      console.log(arguments)  // 會把 2, 5 引數印出來; 
      return a+b
    
    }
    
    console.log(add(2, 5))
    
會得到 [Arguments] { '0': 2, '1': 5 }
7

Argument 的第 0 個為第一行的 a ， 第一個為 b 即最後一行的回傳值 (2, 5) ; 因此第一行的括號內也可以甚麼都不寫:


    function add() {    
      console.log()
      return arguments[0] + arguments[1]  // 會把 2, 5 引數回傳，即上式的 return a+b ; 
    }
    
    console.log(add(2, 5))
    
答案也是 7 。

[1、參考引數和參數的解說:](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/A_re-introduction_to_JavaScript)

[2、參考 arguments 物件解說:](https://ithelp.ithome.com.tw/articles/10192368)

[3、[筆記] 談談JavaScript中函式的參數(parameter),arguments和展開運算子(spread)](https://pjchender.blogspot.com/2016/04/javascriptparameterargumentsspread.html)

---

# 十四、 引數是啥?
---

## Arguments 印出所有參數
引數是個 object 物件，並不是陣列:

    function add (a,b ) {
      console.log(arguments)
    
      return a + b 
    }
    
    console.log(add(2, 5))
    
會印出 `[Arguments] { '0': 2, '1': 5 }` 7 ， 且答案可以相加是 7 。

---
數字 0 跟字串 '0' 是一樣的沒有區別:

    function add (a,b ) {
      console.log(arguments[])  // [ ] 內可以取得 [Arguments] { '0': 2, '1': 5 } 的 0 或是 1 這個 key 變數的位置;
    
      return a + b 
    }
    
    console.log(add(2, 5))
    
    var a = {    // → 假設有個物件長這樣;
      b: '123'
    }
    
    a.b    //  或是可以用 a['b'] ;
這印不出來。

---

    function add (a,b ) {
      console.log(arguments[])  // [ ] 內可以取得 [Arguments] { '0': 2, '1': 5 } 的 0 或是 1 這個 key 變數的位置;
    
      return a + b 
    }
    
    console.log(add(2, 5))
    
    var a = {    // → 假設有個物件長這樣;
      0: '123'
    }
    
    a[0]    //  可以這樣存取;

但其實 a 是一個物件不是陣列:

    function add (a,b ) {
      console.log(arguments)  // [ ] 內可以取得 [Arguments] { '0': 2, '1': 5 } 的 0 或是 1 這個 key 變數的位置;
    
      return a + b 
    }
    
    console.log(add(2, 5))
    
    var a = {    // → 假設有個物件長這樣;
      0: '123'
    }
    
    a[0]    //  可以這樣存取;
    
    console.log(a[0])
    
會印出 123 。

## Arguments 還有個屬性是 .length
一共可以傳幾個參數進來:

    function add (a,b ) {
      console.log(arguments.length)  // [ ] 內可以取得 [Arguments] { '0': 2, '1': 5 } 的 0 或是 1 這個 key 變數的位置;
    
      return a + b 
    }
    
    console.log(add(2, 5))
    
    var a = {    // → 假設有個物件長這樣;
      0: '123'
    }
    
    a[0]    //  可以這樣存取;
    
    console.log(a[0])

會印出:
2 → 表示有兩個參數傳進來;
7 → 因為 return 2 + 5 ;
123 → 宣告 a 被賦予一個物件，而物件內 0= '123' 字串。

---

清楚一點表示:
    
    function add(a, b) {
      console.log(arguments.length)
      return a + b
    }
    
    add(2, 5)
    
會印出 2 ， 表示有兩個參數回傳，是一個長得很像 array 的 obj ， array-like 。

---

# 十五、 function 傳參數的運作機制

## 用個函數交換 2 變數:

    function swap(a, b) {  // 傳 a 和 b 進去函式;
        /*
          var a = number1   → 因為 a 和 b 是複製來的，所以記憶體位置跟 number1 和2 不同;
          var b = number2  → 傳進來後 a 和 b 的確變了，但不會影響外部 var number1 = 10 和 var number2 = 20 的值。
          其 value 一樣，但是是不同的 key 變數。
        */
      var temp = a  // a 賦予暫存變數;
      a = b    
      b = temp // 交換 2 變數需有第三方暫存，;
      console.log('a, b:', a, b) // 這邊的確有交換 ， a, b: 20 10 ;
    }

    var number1 = 10
    var number2 = 20
    console.log(number1, number2)  // 這裡為 10 20 ， 這邊為最開始傳進去第一行函式時期時有複製一份;
    
    swap(number1, number2)
    
    console.log(number1, number2) // 印出來為 10 20 ;
印出來為:
![](https://i.imgur.com/uwjpDIf.png)



    
如果第五行 `console.log('a', 'b')` 的 (a, b) 這樣子直接印會出現:
![](https://i.imgur.com/12TlNXE.png)

而如果是字串 ('a', 'b') 則是:
![](https://i.imgur.com/Px3Z2Rj.png)
都是一樣的表達方式，只是印出來的值不同。

---
## 這樣回傳是 11 還是 10 ?

    function addValue(obj) {    // 傳個 obj 進來;
      /*
        其實這裡有點像是物件的記憶體存取方式，第一行 var obj = a (外部的 number: 10)
        會指向同一個值。      
      */
      obj.number++    // 回傳變數(屬性) +1 ; 也因指向同一個記憶體位置，裡面的值會影響外面宣告的變數值;
      return 1
    }
    
    var a = {
      number: 10    
    }
        
    addValue(a)
    console.log(a)

簡單型別如數字、字串、布林是直接 copy value 因此改不到，但物件和變數型態比較不同。
如變數並賦予給值時，變數會指向值在記憶體中的位置，若以這個值為參照，指定另一變數指向這個值時，電腦會在記憶體中新增(複製)一個新值，讓後來的這個變數指向新的值。  
因此會印出一個物件: { number: 11 } 。

也因此使用函式時，數值內部的東西更動時，可能會與外部的宣告賦予變數連動，也因此有幾種形式:

pass by value
pass by sharing (屬於在 pass by value 底下)
pass by reference (JS 內沒有)

[參考文件: 深入探討 JavaScript 中的參數傳遞：call by value 還是 reference？](https://blog.techbridge.cc/2018/06/23/javascript-call-by-value-or-reference/)

---

    function addValue(obj) {
    
      obj = {
        number: 100
      }
      return 1
    }

    var a = {
      number: 10  // 而這邊新的物件產生，指到了另一塊記憶體;
    }
    
    addValue(a)  // 這行沒有作用，但表示下行 a 會被加入到 function addValue(obj) { 內;
    console.log(a)

因 `obj = {number: 100}` 是個物件，存在一個獨立記憶體中; 而僅賦予 obj 是物件，但因為函式中沒有運算，也因此回傳 1 不起作用; 最後宣告 `var a = {number: 10}` 是個物件，也是個變數，但沒有指向與第三行的 obj 同個記憶體，因此可以回傳到第一行起作用，會印出: { number: 10 } 。






