# <font class="h2">this:call, apply, bind</font>
###### tags: `javascript`
<style>
.h2 {
background: linear-gradient(135deg,#fff,#537479) ;
color: #537479;
display:block;
padding: 6px 5px;
border-radius: 4px;
}
.h3 {
background: linear-gradient(180deg,#fff 50%,#c9d5d4) ;
color: #537479;
display:block;
padding: 6px 5px;
border-bottom: 3px solid #537479;
}
.h4 {
color: #537479;
font-weight:bold;
font-size:1.1em;
}
</style>

call, apply, bind是phototype裡面的方法
<br><br><br><br>
### <font class="h4">「綁定物件this」在不同的情況下是不同的東西,但我們可以自己決定函式的綁定物件是什麼</font>
<br>
:::info
call 就是呼叫函式,不過可透過 call 傳入第一個參數去改變 this,後續的參數當作一般參數 傳入,apply 差別在於後續參數是一個陣列,==bind 大致和 call 相同但會產生新的函式==。
==call、 apply、bind 都是屬於函式的原型方法(Function.prototype)==。
:::
```javascript
function base() {
console.log(this, arguments);
}
base.call({name:"John"}, 2, 3); //會立即執行
base.apply({name:"Mark"}, [2, 3]); //會立即執行
base.bind({name:"Susan"})(2, 3); //bind會產生新的函式,需另外執行
```
<br><br><br><br>
### <font class="h3">1. 使用bind重新綁定物件</font>

- bind不會立刻執行,要去調用它才會執行
- 會return一個新的函式
```javascript
function test(){
console.log(this)
}
test();//window
let test2 = test.bind({x:3});
test2();//{x:3}
```
<br>
### <font class="h3">2. 函式呼叫有apply和call兩種方式</font>
強制指定某個物件作為該function執行時的this
<br>
### <font class="h4">➤使用apply呼叫函式</font>

<br>
```javascript
function add(n1,n2){
console.log("結果",n1+n2); //7
console.log("綁定物件",this); //window
}
add(3,4);
```
基本的函式呼叫,無法自己設定綁定物件

<br>
```javascript
function add(n1,n2){
console.log("結果",n1+n2); //9
console.log("綁定物件",this); //{y: 4}
}
add.apply({y:4},[4,5]);
```

<br><br><br><br><br><br>
### <font class="h4">➤使用call呼叫函式</font>

```javascript
function add(n1,n2){
console.log("結果",n1+n2); //14
console.log("綁定物件",this); //{z:10}
}
add.call({z:10},10,4);
```

<br><br><br><br><br><br><br><br>
---
<br>
call, apply, bind都會影響函式的呼叫方式
- call是立刻執行,然後將this給替換掉
- apply與call也是類似的觀念,而apply與call不同的是帶入參數會是陣列呈現
- ==bind不會立刻執行,要去調用它才會執行==
<br>
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function fn(para1, para2) {
console.log(this, para1, para2);
}
fn(1,2)//window,1,2
```
這邊`fn(1,2)`方式是屬於simple call,會指向全域`window`
<br>
### <font class="h4">使用call的方式</font>
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function fn(para1, para2) {
console.log(this, para1, para2);
}
fn.call(family, 1, 2)//{myName: '小明家'} 1 2
```
使用`call`在執行的時候,第一個參數就是this,所以這裡`family`就會替代掉`console.log(this, para1, para2);`的this
所以原本window就會替換成`family`物件
<br>
### <font class="h4">使用apply的方式</font>
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function fn(para1, para2) {
console.log(this, para1, para2);
}
fn.apply(family,[3,4])//{myName: '小明家'} 3 4
```
apply與call也是類似的觀念,而apply與call不同的是帶入參數會是陣列呈現
<br>
### <font class="h4">使用bind的方式</font>
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function fn(para1, para2) {
console.log(this, para1, para2);
}
var fn2 = fn.bind(family,'小明','杰倫');
fn2();//{myName: '小明家'} '小明' '杰倫'
```
bind不會立刻執行,因此我們在使用bind時,可以先宣告一個變數`fn2`,之後`fn2()`再去調用它才會執行
<br>
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function fn(para1, para2) {
console.log(this, para1, para2);
}
var fn2 = fn.bind(family,'小明','杰倫');
fn2(1,2);//{myName: '小明家'} '小明' '杰倫'
```
這裡我們是另外帶入參數`1,2`看看,會發現沒有變化
我們在使用bind時就會先強制把參數寫入,如`小明,杰倫`
<br>
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function fn(para1, para2) {
console.log(this, para1, para2);
}
var fn2 = fn.bind(family,'小明');
fn2(1,2);//{myName: '小明家'} '小明' 1
```
當然我們在`bind`寫入參數時使用部分帶入,這裡只寫入第一個參數`小明`,第二個參數先不要帶入。
第二個參數,會依序取用第二次執行帶入的參數
:::info
:bulb:這也是`bind`容易讓人混淆的地方,`fn2(1,2)`看起來像simply call,但實際`fn.bind(family,'小明')`的部分已經決定this的調用方式
:::
<br><br><br><br>
### <font class="h3">透過call實現new創建物件</font>
```javascript
function MyObject(name,age){
this.name = name
this.age = age
}
var obj = new MyObject("Mark",30)
```
<br><br>
使用 new 建立物件,其實也就是等同於:
```javascript
var a = {}
MyObject.call(a,"Mark",30)
```
<br><br><br><br>
### <font class="h3">嚴謹模式 strict mode</font>
使用call方式將純值給傳入
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function fn(para1, para2) {
console.log(this, para1, para2);
}
fn.call(1,'小明','杰倫')
```

數字`1`傳入之後會以「建構式物件」方式呈現
<br>
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function fn(para1, para2) {
console.log(this, para1, para2);
}
fn.call('文字','小明','杰倫')
```

如果以文字傳入,一樣會是「建構式物件」方式呈現
<br>
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function fn(para1, para2) {
console.log(this, para1, para2);
}
fn.call(undefined,'小明','杰倫')//window,小明,杰倫
```
如果傳入`undefined`,this則會指向`window`
:::info
:bulb:使用call呼叫函式的this時,若是在「非嚴格模式」下傳入的是`null`、`undefined`,將會被替換為全域變數,而原使型態的值將會被以建構式物件方式封裝,如前面使用數字`1`跟`文字`,就會以建構式方式被封裝。
:::
<br>
因為javascript是屬於相對寬鬆的程式,因此許多不嚴謹的程式碼都可以被執行,在ES5之後就提供開發者這種語法受限的模式(嚴格模式)
- 加入`'use strict'`即可運作
- 並不會影響不支援嚴格模式的瀏覽器
- 可依據執行環境設定`'use strict'`
- 透過拋出錯誤的方式消除一些安靜的錯誤(防止小錯誤)
- 禁止使用一些有可能被未來版本ECMAScripr定義的語法,也就是說你在ES5的環境下加入`'use strict'`,ES6,ES7可能被定義的語法,會禁止你來使用。
:::info
嚴格模式在每個瀏覽器的執行結果都不太一樣
:::
```javascript
(function () {
'use strict';
})
```
這邊使用即時函式來限制執行環境,當我在裡面加入`'use strict'`的字串時候,這一段執行環境就會進入嚴格模式。
<br>
```javascript
(function () {
'use strict';
a ='小明'
})//會報錯
```
在嚴格模式下,會要求你必須先宣告變數,才能夠賦予值
### <font class="h4">範例:</font>
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function callStrict(para1, para2) {
'use strict';
console.log(this, typeof this, para1, para2);
}
callStrict.call(1, '小明', '杰倫')//1 'number' '小明' '杰倫'
```
這裡使用嚴格模式的話,數字`1`就不會被以建構式物件方式封裝,而是型別會維持原來的型別(數字型別的`1`)
<br>
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function callStrict(para1, para2) {
'use strict';
console.log(this, typeof this, para1, para2);
}
callStrict.call(undefined, '小明', '杰倫')//undefined, undefined, '小明' '杰倫'
```
這邊試這帶入`undefined`使用嚴格模式,this就不會指向window,而維持`undefined`
<br>
```javascript
var myName = '真心話大冒險'
var family = {
myName: '小明家',
}
function callStrict(para1, para2) {
'use strict';
console.log(this, typeof this, para1, para2);
}
callStrict('小明', '杰倫')//undefined, undefined, '小明' '杰倫'
```
這邊如果直接使用`callStrict()`來執行(simple call的方式)
一般模式下使用simple call方式呼叫this,this會指向`window`
而==在嚴格模式下,原本的window就會變成`undefined`==
:bulb:其實在使用簡易呼叫時,就如同使用call的方法,只不過簡易呼叫並不會傳入this這個參數,因此this的參數是使用undefined來替代,在進入嚴格模式下`undefined`就會維持原來的`undefined`
:::info
所以簡易呼叫盡可能不要去調用this,因為它的本質就是`undefined`
:::
<br><br><br>