---
# System prepended metadata

title: Javascript 第五章 函式的運用
tags: [JS X Codeshiba]

---

# Javascript 第五章 函式的運用
###### tags: `JS X Codeshiba`
## 甚麼是函式
**函式:** 是一種五臟俱全的 **`陳述式組合`**，會被視為一個單位來執行，可以把他想成是**副程式**

每個函式都有一個本文
```javascript=
function sayHello(){
//這是本文，他會從左邊的大括號開始
console.log("Hello World");
// 在右邊的大括號結束
}
```
這是一個**宣告函式**的範例: 我們宣告了一個名為sayHello的函式
但是宣告不會被執行，我們必須使用函式的名稱與括號來**呼叫函式(call)**
也稱為運行(running)、執行(executing)、引用(invoking)或指派(dispatching)

```javascript=
sayHello(); //主控台就會印出Hello World!
```
## 回傳值
在函式的本文中，return關鍵字會立刻中止函式，並且回傳指定的值
如果沒有return，那麼回傳值就會是undefined
```javascript=
function getGreeting(){
    return "Hello world!";
}
```
## 呼叫 v.s. 參考 (把資訊帶出)
```javascript=
//以下是呼叫範例
const f=getGreeting;
f(); //"Hello World!"

//以下是參考範例
const 0={};
o.f=getGreeting;
o.f(); //"Hello World!"

const arr = [1,2,3];
arr[1]=getGreeting; //現在arr是[1, function getGreeting(), 2]
arr[1](); //"Hello, World!"
```

## 函式引數(把資訊帶入)
如何把資訊傳入函式呢? 把資訊傳入函式呼叫式的主要機制式函式引數(有時稱為參數)

```javascript=
//這是一個接受兩個引數，並且回傳兩個數字的平均值的函式
function avg(a,b){
    return (a+b)/2;
}
```

當函式被呼叫時，引數會吸收值，然後轉換成**實際引數**
```javascript=
avg(5,10); //7.5
```

**引數只會在函式裡面生存**，就算他們名稱與函式外的變數名稱相同
```javascript=
const a=5, b=10;
avg(a,b);//這裡的a, b跟上面的a, b是沒有關係的
```
```javascript=
function f(x){
    console.log(`inside f: x=${x}`);
    //3
    x=5;
    console.log(`inside f: x=${x}(after assignment)`);   
    //5
}

let  x = 3;
console.log(`before calling f: x=${x}`);
//3
f(x);
console.log(`after calling f: x=${x}`);
//3
```

引數x(函式內)跟變數x(函式外)有所不同
當x在f裡面被賦值，會指向一個新的、不同的物件
函式外的x仍然會指向原本的物件

### 引數構成函式嗎?
f()、f(x)是不同的函式，後者也與f(x,y)是不同的函式
有一個函式叫做f時，可以用0、1或10個引數來呼叫，而且呼叫的還是同一個函式
如果沒有提供引數，他們會私下接收undefined值

```javascript=
function f(x){
    return `in f: x=${x}`;
}
console.log(f()); //undefined
console.log(f(2)); //in f: x=2
```
### 解構引數
```javascript=
function getSentence({subject, verb, object}){
    return `${subject} ${verb} ${object}`;
}

const o = {
    subject: "I",
    verb: "love",
    object: "JavaScript",
}

console.log(getSentence(o));
//I love JavaScript
```
如果變數無法對應被傳進來的物件中的特性，會得到undefined

解構陣列也是可以的

```javascript=
function getSentence([subject, verb, object]){
    return `${subject} ${verb} ${object}`;
}

const arr =["I", "love", "JavaScript"];
console.log(getSentence(arr));
```
也可以使用擴張運算子(...)來蒐集所有的引數，如果要使用擴張運算子
它必須是最後一個引數，如果放在其他引數前面，JS無法分辨它與其他引數的差別

```javascript=
function addPrefix(prefix, ...words){
    const prefixedWords = [];
    for(let i=0; i<words.length; i++){
        prefixedWords[i] = prefix + words[i];
    }
    return prefixedWords;
}
addPrefix("con","verse","vex");
//["converse","convex"]
```

### 預設引數
屬於ES6的功能，可以指定引數的預設值
當沒有提供引數的值時，他們會得到undefined值，你可以指定其他的預設值:
```javascript=
function f(a, b="default", c=3){
    return `${a}-${b}-${c}`;
}

console.log(f(2,3,4)); //2,3,4
console.log(f(5,6)); //5,6,3
console.log(f(a,5,8)); //跑不出來
console.log(f()); //undefined-default-3
```


## 把函式當成物件的特性
如果函式是物件的特性，則稱為**方法**，這會與一般的函式區分
```javascript=
const o = {
    name: 'Wallace', //原始特性
    bark: function(){return 'Woof!'; }, //函式特性(方法)
}
console.log(o.name,o.bark);
```

## this關鍵字
this關鍵字跟物件特性函式有關，當呼叫方法時，this關鍵字就代表呼叫的方法所屬的物件值
```javascript=
const o = {
    name: 'Wallace',
    speak(){return `My name is ${this.name}!`;},
}
//當我們呼叫o.speak的時候，this關鍵字會綁定o
o.speak(); //My name is 'Wallace'!
```
this的綁定是根據:
**呼叫**函式的方法(o)
宣告函式的方法(x)

```javascript=
const o = {
    name: 'Wallace',
    speak(){return `My name is ${this.name}!`;},
}

const speak = o.speak;
speak === o.speak;

console.log(speak()); //My name is !
// 呼叫函式的方式變了，JS不知道函式是在o裡面宣告的，所以this會綁定undefined
console.log(o.speak()); //My name is Wallace!
// 呼叫函式的方式有明確的o，所以JS知道是在o裡面宣告的，所以this會被綁定，因此出現Wallace
```

### 注意事項
在嵌套的函式中使用this變數時，有一種經常讓人犯錯的地方，就是this會被綁到其他地方(全域物件或成為undefined)

```javascript=

const o = {
    name: 'Julie',
    greetBackwards: function(){
        function getReverseName(){
            let nameBackwards = '';
            for(let i=this.name.length-1; i>=0; i--){
                nameBackwards += this.name[i];
            }
            return nameBackwards;
        }
        return `${getReverseName()} si eman ym, olleH`;
    },
};
o.greetBackwards();

```
此時可以把第二個變數指派給this:
```javascript=
const o = {
    name: 'Julie',
    greetBackwards: function(){
        const self = this;
        function getReverseName(){
            let nameBackwards = '';
            for(let i=self.name.length-1; i>=0; i--){
                nameBackwards += self.name[i];
            }
            return nameBackwards;
        }
        return `${getReverseName()} si eman ym, olleH`;
    },
};
console.log(o.greetBackwards());
```

## 函式運算式與匿名函式
一般來說，匿名函式會有:
1. 本文(定義函式要做的事)
2. 識別碼(讓我們可以呼叫)



要放入函式內的參數用函式來表達，這則稱為匿名函式
![](https://i.imgur.com/ug6XH84.png)



## 表達式(Expression)與陳述式(Statetment)
表達式(Expression) 會回傳值
    1+1=2
陳述句(Statement) 不會回傳值
    function()

因為變數提升(hoisting)的特性，所以陳述式可以把原先在下方的function過程直接拿來套用，但是屬於**賦值**過程的表達式不行
[參考文章_Huli_我知道你懂 hoisting，可是你了解到多深？](https://blog.techbridge.cc/2018/11/10/javascript-hoisting/)
![](https://i.imgur.com/yHbgQph.png)


函式很重要，以下分為幾個階段

1. 內建函式
```javascript=
alert("Hello World")
```
2. 函式的基礎
3. 設計基礎
```javascript=
    function test(){
      var sum=0;
      for(var n=0;n<=100;n++){
        sum=sum+n
      }
      alert(sum);
    }
```

可以加入**參數名稱**，想清楚要呈現甚麼樣的值
```Example2
  <script>
    function test(message){
      alert(message);
    }
    test("Hello World");
  </script>
```

4. 呼叫使用階段
```Example1
test();
```

傳入函式的**參數資料**
```Example2
test("Hello World");
```
5. 整合範例-如何做一個可以自動從1加總到任意數的函式
```
  <script>
    var sum=0;
    function Getsumup(){
      for(var n=1;n<=100;n=n+1){
        sum=sum+n;
      }
      alert(sum);
      return sum;
    }
    var result=Getsumup()
    alert(result);
  </script>
```


## 第二種寫法

  函式是一種資料，也可放入變數裡面

```
 <script>
    var add=function(n1,n2){
      //return n1+n2;
      alert(n1+n2);
    }
    var test=add;
    test(1,2);
  </script>
```

## 函式的注意事項

不可以重複宣告
var x = 3
var x = 5

這很不ok，會錯亂

## 全域變數 & 區域變數

變數宣告的方法
1. var
2. const
3. let

但是可以再函式內宣告，因為函式的{}內部是一個新的空間

最外層: 全域變數
次一層: 區域變數

在Function裡面宣告為**區域變數**

![](https://i.imgur.com/reOmX6h.png)

變數屬性範例
![](https://i.imgur.com/3lbsSYS.png)

## 回呼函數
![](https://i.imgur.com/eWGmF1o.png)

## 匿名函式


## 立即函式(IIFE)
全名: Immediately Invoked Functions Expressions 
可以立即呼叫的函式表達式

表達式=>會回傳值
使用函式表達式以後立即執行
結構如下

![](https://i.imgur.com/jSVXp6C.png)

## 變數提升(Hoisting)
不管你在哪裡做的變數，都會被作為在第一行做宣告

先來看看let和var的不同
![](https://i.imgur.com/o54z4Xp.png)

再來是各個變數在不同宣告的差異
![](https://i.imgur.com/ehkFx7p.png)

Undefined 不等於 Not defined!!
![](https://i.imgur.com/mgVPZwh.png)

## 匿名函式
