---
# System prepended metadata

title: 你所不知道的 JS 讀書筆記-03-入門-作為值的函式、this 識別字、原型
tags: [你所不知道的JS, javascript]

---

# 你所不知道的 JS 讀書筆記-03-入門-作為值的函式、this 識別字、原型
p.50 ~ p.57
![封面](https://i.imgur.com/XgGEzWR.jpg)
## 作為值的函式
一個函式應該被當作一個**運算式**（expression）。
```javascript
var foo = function() {
    // ..
};
var x = function bar() {
    // ..
};
```
第一個函式運算式是**匿名的**，第二個是**具名的**。兩者各有所好。
### 即刻調用的函式運算式（IIFE）
載入程式碼的當下直接執行該程式。
```javascript
(function foo(){
    console.log("Hello IIFE");
})();

/* 以上相當於下段程式碼 */

function foo() {
    console.log("Hello IIFE");
}
foo();
```
外層的`(..)`避免`foo()`被視為一個普通的函式宣告，是 Javascript 的一種語法。後方結尾處的`()`實際執行前面所參考的函式運算式。
IIFE 會建立變數範疇，內部的變數宣告不會影響到外部的變數。
IIFE 也能`return`值。
## 閉包（closure）
函式執行完畢之後，記住並持續存取一個函式範疇以及其變數的方式。
```javascript
function makeAdder(x) {
    function add(y) {
        return x + y
    }
    return add;
}
// 簡寫 var makeAdder = x => y => x + y
```
呼叫外層`makeAdder()`時回傳的內層`add()`函式都能記住傳入`makeAdder()`的參數`x`是什麼值。
> 內層函式`add()`用到屬於外層的`x`，於是`add()`閉關包含了`x`。
```javascript
var plusOne = makeAdder(1);
var plusTen = makeAdder(10);
console.log(plusOne(3)); // 4
console.log(plusTen(3)); // 13
```
宣告`plusOne`呼叫`makeAdder(1)`時，取得內層`add()`一個參考，`add()`藉由`makeAdder()`知道參數`x`為 1 。
宣告`plusTen`呼叫`makeAdder(10)`時，取得內層`add()`一個參考，`add()`藉由`makeAdder()`知道參數`x`為 10 。
透過`console.log()`呼叫`plusOne(3)`時，`plusOne(3)`把 3 作為內層的參數`y`，使其與`x`（這裡為 1 ）相加，故得 4。
透過`console.log()`呼叫`plusTen(3)`時，`plusOne(3)`把 3 作為內層的參數`y`，使其與`x`（這裡為 10 ）相加，故得 13。

## 模組（module）
閉包的常見用法，模組能隱蔽程式碼的實作細節，提供一個可從外部取用的公開 API。
```javascript
function User() {
    var username, password;
    function doLogin( user, pw ) {
        username = user;
        password = pw;
        // 進行其餘的登入作業
    }
    var publicAPI = {
        login: doLogin
    };
    return publicAPI;
}
var fred = User() // 建立一個宣告為 fred 的模組實體
fred.login( "fred", "123qwe" )
```
fred 是一個獨立實體，有一整個新的範疇被建立，包括了`User()`外層範疇、他的變數`username`與`password`、內部範疇`doLogin()`，而且都無法從外部的世界存取。如果宣告 jenny 為`User()`也會建立一個與 fred 完全無關的獨立實體，有自己的範疇與上述內容。
> 內層函式`doLogin()`用到屬於外層的`username`與`password`，於是`doLogin()`閉關包含了`username`與`password`。

publicAPI 是一個物件，帶著`login`特性，對內層的`doLogin()`函式有一個參考。
## this 識別字
如果一個函式內有一個`this`參考，這個`this`通常指向一個物件（不是參考函式本身），指向哪個物件取決於函式被呼叫的方法。

---

1. 當`this`在函式內時，`this`為全域物件
```javascript
function foo() {
    console.log( this.bar );
}
var bar = "global"
foo(); // "global"
```
`foo()`將`this`設為全域物件，但在 strict 模式中會是`undefined`。

---

2. 當`this`在函式內且在一個物件內被調用時，`this`為該物件
```javascript
function foo() {
    console.log( this.bar );
}
var obj1 = {
    bar: "obj1",
    foo: foo
};
obj1.foo() // "obj1"
```
`obj1.foo()`啟用`foo()`函式，位置在`obj1`物件內，所以指向的物件是`obj1`。

---

3. 將物件以參數帶入函式時，`this`為該參數
```javascript
function foo() {
    console.log( this.bar );
}
var obj2 = {
    bar: "obj1",
};
foo.call(obj2) // "obj2"
```
使用給定的`this`參數（與其他參數）來呼叫`foo`這個函數。指向的物件是`obj2`。

---

4. 以 new 設立新的空物件並調用函式，`this`為該空物件
```javascript
function foo() {
    console.log( this.bar );
}
var bar = "global"
new foo(); // undefined
```
new foo() 把 this 指向一個全新的空物件。
> 原則：檢視目標函式被呼叫的方法，來判斷`this`的指向
## 原型
當你參考一個物件上的某個特性，如果該特性不存在，Javascript 會自動使用該物件內部的原型參考，在另外類似的物件尋找該特性，作為後備機制。
```javascript
var foo = { a: 42 };
var bar = Object.create( foo );
bar.b = "hello world";
bar.b; // "hello world"
bar.a; // 42
```
`a`特性不存在於`bar`物件上，但`bar`的原型連結到`foo`，所以會自動在`foo`物件上找`a`。
###### tags: `javascript` `你所不知道的JS`