DAY 22
0
Modern Web

# 3. 顯性函數綁定 (Explicit Function Binding)

## 3.1. Function.prototype.bind() 篇

### 傳統函數

`Function.prototype.bind()` 可以為一個函數建立新函數物件，新函數物件會繼承原函數的 prototype，同時任意綁定一個固定的擁有者。

``````var getFullName = function() {
return this.firstName + " " + this.lastName;
}

var firstName = "One", lastName = "Jar";
var introIronMan = getFullName.bind( { firstName: "Tony", lastName : "Stark" } );
var introCaptainAmerica = getFullName.bind( { firstName: "Steven", lastName : "Rogers" } );

console.log(getFullName());           // "One Jar"
console.log(introIronMan());          // "Tony Stark"
console.log(introCaptainAmerica());   // "Steven Rogers"
``````

### Arrow Functions

``````var getFullName = () => {
return this.firstName + " " + this.lastName;
}

var firstName = "One", lastName = "Jar";
var introIronMan = getFullName.bind( { firstName: "Tony", lastName : "Stark" } );
var introCaptainAmerica = getFullName.bind( { firstName: "Steven", lastName : "Rogers" } );

console.log(getFullName());           // "One Jar"
console.log(introIronMan());          // "One Jar"
console.log(introCaptainAmerica());   // "One Jar"
``````

## 3.2. Function.prototype.apply() / Function.prototype.call() 篇

### 傳統函數

``````var whatsThis = function() {
return this;
};
var getFullName = function() {
return this.firstName + " " + this.lastName;
}

var ironMan = { firstName: "Tony", lastName : "Stark" };
var captainAmerica = { firstName: "Steven", lastName : "Rogers" };

console.log(whatsThis.apply(ironMan) === ironMan);                // true
console.log(getFullName.apply(ironMan));               // "Tony Stark"
console.log(whatsThis.apply(captainAmerica) === captainAmerica);  // true
console.log(getFullName.apply(captainAmerica));        // "Steven Rogers"
``````

### Arrow Functions

``````var whatsThis = () => {
return this;
};
var getFullName = () => {
return this.firstName + " " + this.lastName;
}

var ironMan = { firstName: "Tony", lastName : "Stark" };
var captainAmerica = { firstName: "Steven", lastName : "Rogers" };

console.log(whatsThis.apply(ironMan) === window);        // true
console.log(getFullName.apply(ironMan));          // "undefined undefined"
console.log(whatsThis.apply(captainAmerica) === window); // true
console.log(getFullName.apply(captainAmerica));   // "undefined undefined"
``````

# 4. 函數作為建構子

### 傳統函數

``````var Hero = function(n){
this.exp = n;
};

var h = new Hero(100);
console.log(h);         // Hero {exp: 100}
console.log(h.exp);     // 100
``````

### Arrow Functions

Arrow Function 所宣告的函數不能拿來當建構子，也不存在 `this` 的問題。

``````var Hero = (n) => {
this.exp = n;
};

var h = new Hero(100); // TypeError: Hero is not a constructor
``````

# 5. 回呼函數 (Callback Function) 裡的 this

## 5.1. 簡單呼叫 Callback Function

### 傳統函數

``````var name = "Hi I am Global";

var sayHi = function(){
return this.name;
}

var hero = {
name: "Hi I am a Hero",
act: function(cbk){
return cbk();
}
};

console.log( sayHi() );           // Hi I am Global
console.log( hero.act(sayHi) );   // Hi I am Global
``````

### Arrow Functions I

``````var name = "Hi I am Global";

var sayHi = function(){
return this.name;
}

var hero = {
name: "Hi I am a Hero",
act1: function(cbk){
return cbk();
},
act2: (cbk) => {	// arrow function
return cbk();
}
};

console.log( sayHi() );           // Hi I am Global
console.log( hero.act1(sayHi) );  // Hi I am Global
console.log( hero.act2(sayHi) );  // Hi I am Global
``````

### Arrow Functions II

``````var name = "Hi I am Global";

var sayHi = () => {	// arrow function
return this.name;
}

var hero = {
name: "Hi I am a Hero",
act1: function(cbk){
return cbk();
},
act2: (cbk) => {	// arrow function
return cbk();
}
};

console.log( sayHi() );           // Hi I am Global
console.log( hero.act1(sayHi) );  // Hi I am Global
console.log( hero.act2(sayHi) );  // Hi I am Global
``````

## 5.2. 用 apply() / call() 將物件本身傳入 Callback Function

### 傳統函數

``````var name = "Hi I am Global";

function sayHi(){
return this.name;
}

var hero = {
name: "Hi I am a Hero",
act: function(cbk){
return cbk.apply(this); // 將物件本身傳入 Callback Function
}
};

console.log( sayHi() );           // Hi I am Global
console.log( hero.act(sayHi) );   // Hi I am a Hero
``````

### Arrow Functions I

• 當函數 B 也是傳統函數 (`hero.act1()`) ：`hero.act1()` 自己的 `this` 看呼叫者是誰，也就是 `hero`，所以 `apply()``hero` 綁定為 Callback Function 的 `this`，因此印出 `"Hi I am a Hero"`
• 當函數 B 是 Arrow Function (`hero.act2()`) ：`hero.act2()` 自己的 `this` 沿用外層，也就是 Global 物件 (無論一般模式或嚴謹模式)；再透過 `apply()` 將 Global 物件綁定於 `sayHi()``this`，因此印出 `"Hi I am Global"`
``````var name = "Hi I am Global";

var sayHi = function(){
return this.name;
}

var hero = {
name: "Hi I am a Hero",
act1: function(cbk){
return cbk.apply(this); // 將物件本身傳入 Callback Function
},
act2: (cbk) => {	// arrow function
return cbk.apply(this); // 將物件本身傳入 Callback Function
}
};

console.log( sayHi() );           // Hi I am Global
console.log( hero.act1(sayHi) );  // Hi I am a Hero
console.log( hero.act2(sayHi) );  // Hi I am Global
``````

### Arrow Functions II

``````var name = "Hi I am Global";

var sayHi = () => {	// arrow function
return this.name;
}

var hero = {
name: "Hi I am a Hero",
act1: function(cbk){
return cbk.apply(this); // 將物件本身傳入 Callback Function
},
act2: (cbk) => {	// arrow function
return cbk.apply(this); // 將物件本身傳入 Callback Function
}
};

console.log( sayHi() );           // Hi I am Global
console.log( hero.act1(sayHi) );  // Hi I am Global
console.log( hero.act2(sayHi) );  // Hi I am Global
``````

• 傳統函數：看的是函數 B，也就是呼叫方怎麼呼叫函數 A。
• 箭頭函數：看的是函數 A，也就是函數自身定義的語彙位置。

# 總結

## 一句話總結傳統函數和箭頭函數在 `this` 判斷上的差別

• 傳統函數：看呼叫時的物件是誰。
• 箭頭函數：看函數本身定義的語彙位置。