---
# System prepended metadata

title: '`This`'
tags: [javascript]

---

# `This`
###### tags: `javascript`

JavaScript中的`this`比一般C, Java中的`this`還複雜許多，因為在js中this的指向會隨runtime一直在改變的，簡單分類會有五種指向或更改指向方式。
以下的測試環境皆在Google Chrome。
> doku wiki 支援的markdown so 糟糕，所以so醜....
> 
## 5種binding方法

### Default Binding

* 套用的時機：甚麼都不做的時後
    * 很單純的call一個function的時候，不是以物件的method被call的時候。
    * 刻意忽略的時候：使用`call`、`apply`傳入`null`時。

* `this`的值：
    * strict mode: **`undefined`**
    * unstrict mode: **global 物件**

* Example 1: 非嚴格模式
    ```javascript
    var  myString = "global string";
    
    function f ( ) {
        
        var myString = "local string";
        
        console.log ( this );            // output: window
        console.log ( this.myString );   // output: "global string"
        
    }

    f.myString = "attribute string";

    f ( );    
    ```
    
    > `f` 在這邊是被當作一般的function被call的，所以他的`this`就會指向global物件，在網頁端global物件就會是`window`，所以`this.myString`就會是第一行的那個。
    
    > 這邊用了很醜的`var`，可以改成`let`試試看，會有不一樣的結果呢。
    > 全域宣告的變數會自動變成global的屬性。
    > 試試看：
    > ```javascript
    > console.log( window.a );
    > 
    > var a = "123";
    > console.log ( a );
    > console.log ( window.a );
    > 
    > let b = "456";
    > console.log ( b );
    > console.log ( window.b );
    > ```
     
* Example 2: 嚴格模式
    ```javascript
    'use strict'

    var  myString = "global string";
    
    function f ( ) {
        
        var myString = "local string";
        
        console.log ( this );             // output: undefined
        console.log ( this.myString );    // TypeError: Cannot read property 'myString' of undefined
        
    }

    f.myString = "attribute string";

    f ( );    
    ```
    > 因為是嚴格模式，所以`this`就會是`undefined`，再透過他去取值，就會出錯。
    
### Implict Binding

* 套用的時機：一個function被當作一個物件的method被call的時候。

* `this`的值：該方法是透過哪一個物件被call的就是那個物件。

* Example: 
    ```javascript
    function f () {
        console.log ( this.myString );
    }

    function f2 ( func ) {
        func ( );
    }

    f.myString = "attribute string";

    var myString = "global string";
    var obj = { func: f, myString: "local string" };
    
    f ( );               // output: "global string"
    obj.func ( );        // output: "local string"

    f2 ( obj.func );     // output: "global string"

    var f3 = obj.func;
    f3 ( );              // output: "global string"
    ```
    
    > 第一個output所套用的this綁定就是Default Binding。
    
    > 第二個output，因為是夠過`obj`這個物件去call的，所以`this`的指向就會是`obj`。
    
    > 第三個和第四個output，雖然`func`、`f2`是從`obj`這個物件裡面的值得來，但是最後在call的時候並不是透過obj去call，而是最一般的function call，所以會套用Default Binding（Implicitly Lost）。

### Explicit Binding

* 套用的時機：使用`apply`、`call`、`bind`時。

* `this`的值：傳入時的值。

* `bind`所綁定的`this`不會再被更改，此為*hard binding*，可以看Example 3。

* Example 1: 
    ```javascript
    function f () {
        console.log ( this.myString );
    }

    f.myString = "attribute string";

    var myString = "global string";
    var obj1 = { func: f, myString: "local string 1" };
    var obj2 = { func: f, myString: "local string 2" };
    
    obj1.func ( );        // output: "local string1"
    f.call ( null );      // output: "global string"
    f.call ( obj2 )       // output: "local string2" 
    
    var f2 = obj.func;
    f2.apply ( ob2 );     // output: "local string2"
    ```
    
    > 第一個output所套用的this綁定就是Implict Binding。 

    > 第二個output所套用的則是Default Binding
    
    > 第三個和第四個output因為是透過`call`和`apply`去做的，所以他們的`this`會是套用Explicit Binding，他們的`this`的值就會是傳入的那個。

 * Example 2: 
    ```javascript
    function f () {
        console.log ( this.myString );
    }

    f.myString = "attribute string";

    var myString = "global string";
    var obj = { func: f, myString: "local string" };
    
    let bindFunc = f.bind ( obj );
    
    bindFunc ( );    // output: "local string"
    ```
    
    > `bindFunc`已經被bind到`obj`上，所以call的時候的`this`就會是`obj`。

 * Example 3:
    ```javascript
    function f () {
        console.log ( this.myString );
    }

    f.myString = "attribute string";

    var myString = "global string";
    var obj = { func: f, myString: "local string1" };
    var obj2 = { func: f, myString: "local string2" };
    
    let bindFunc = f.bind ( obj );
    
    bindFunc.apply ( obj2 );    // output: "local string1"
    ```
    
    > `bindFunc`已經被bind到`obj`上，而且因為是*hard binding*，所以就算使用apply去call，`this`還會是`obj`

 * Example 4: 自己想啦，Lexical Binding那邊的example有解釋
    ```javascript
    function f () {
        this.myString = "local string";
    }

    var obj = { myString: "object string" };
    var myString = "global string";
    
    console.log ( obj.myString )    // output: ?
    f.call ( obj )
    console.log ( obj.myString )    // output: ?
    ```
    
* Example 5: 猜猜看 [ref](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#hard-binding)
    ```javascript
    function foo(something) {
        console.log( this.a, something );
        return this.a + something;
    }

    var obj = {
        a: 2
    };

    var bar = function() {
        return foo.apply( obj, arguments );
    };

    var b = bar( 3 );    // output: ?
    console.log( b );    // output: ?
    ```
    
### `new` Binding

* 套用的時機：當function被`new`的時候。

* `this`的值：被創建出來的那個物件。

* 當function被`new`的時候，就是可以把這個function當做一個class，所以它會產生一個物件，這時候，`this`就會是那個物件。就像其他語言，在物件裡面的`this`就會是自己一樣。

* Example : 
    ```javascript
    function f () {
        this.myString = "local string";
    }

    f.myString = "attribute string";

    var myString = "global string";

    let newObj = new f ( );
    
    console.log ( newObj.myString );    // output: "local string"
    ```
    
    > `newObj`是從`f` `new` 出來的，所以可以把`f`當作一個class，而`newObj`就是`f`的一個instance，這個時候`this`就會是那個`new`出來的物件。

### Lexical Binding

* 套用的時機：使用arrow function的時候。

* `this`的值：看是誰來包這個arrow function那就是誰。

* Example : 
    ```javascript
    function f () {
        this.myString = "local string";
        
        return () => {
            this.myString = "arrow function"
        }
    }

    var obj = { myString: "object string" };
    var myString = "global string";
    
    var arrowFunc = f.call ( obj );
    console.log ( obj.myString );       // output: "local string"
    
    arrowFunc ( );
    console.log ( obj.myString );       // output: "arrow function"
    ```
    
    > 第一個output的結果是套用Explicit Binding，因為`f`是使用`call`被call的，所以`f`在執行的時候的this就會是傳進去的`obj`。所以就會把`obj`的`myString`的值改掉。
    
    > `arrowFunc`會是`f`執行完回傳的結果，回傳了一個arrow function，所以`arrowFunc`現在是一個function，而且還沒有被執行。
    
    > 第二個output。因為前面call了arrowFunc這個function。這個function執行的`this`會套用Lexical Binding，因為包裝這個arrow function的時候的`this`是`obj`，所以在執行這個arrow function的時候的`this`就會是`obj`。所以`obj`的`myString`的值又被改掉了。

## 各種Binding的優先順序
* Lexical Binding 、`new` Binding  > Explicit Binding > Implicit Binding。
* 以下提供各種example，請自行判斷是哪種binding，再根據優先順序得出解答。
* Example 1: 
    ```javascript
    function f () {
        console.log ( this.myString );
    }

    var obj = { myString: "object string", func: f };
    var obj1 = { myString: "object string1", func: f };
    
    obj.func ( );            // output: ?
    obj.func.call ( obj1 );  // output: ?
    ```
    
* Example 2: 
    ```javascript
    function f () {
        this.myString = "function string";
    }

    var obj = { myString: "object string", func: f };
    
    var newObj = new obj.func ( );
    
    console.log ( obj.myString );       // output: ?

    obj.func ( );
    console.log ( obj.myString );       // output: ?
    
    console.log ( newObj.myString );    // output: ?
    ```
    
* Example 3: 
    ```javascript
    var counter = 1;
    
    function f () {
        this.myString = `function string ${counter++}`;
    }

    var obj = { myString: "object string", func: f };
    
    var bindFunc = f.bind ( obj );
    var newObj = new bindFunc ( );

    console.log ( obj.myString );        // output: ?
    console.log ( newObj.myString );     // output: ?
    
    obj.func ( );
    console.log ( obj.myString );        // output: ?
    ```

* Example 4: 這個例子有的東西會有Error喔。
    ```javascript
    function f ( ) {
        return () => {
            console.log(this.myString);
        }
    }
    
    var obj1 = { myString: "object string 1" },
    var obj2 = { myString: "object string 2" };
        
    var arrowFunc = outer.apply ( obj1 );
    
    arrowFunc();            // output: ?
    arrowFn.call ( obj2 );  // output: ?
    new arrowFn();          // error, why?
    ```
    
* Example 5: [ref](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#everything-in-order)
    ```javascript
    function foo(something) {
        this.a = something;
    }

    var obj1 = { };

    var bar = foo.bind( obj1 );
    bar( 2 );
    console.log( obj1.a );     // output: ?

    var baz = new bar( 3 );
    console.log ( obj1.a );    // output: ?
    console.log ( baz.a );     // output: ?
    ```

* Example 6: 大雜燴啦，這個例子也有東西會有Error喔。
    ```javascript
    var counter = 1;

    function f ( ) { 
        
        this.myString = `local string ${counter++}`;
        
        console.log ( this.myString );
        
        return () => this.myString; 
    
    };

    var f2 = ( ) => { 
        
        this.myString = `local string ${counter++}`;
   
        console.log ( this.myString );
        
        return ( ) => this.myString; 
    }
    
    var obj1 = { myString: "object string 1", func: f };
    var obj2 = { myString: "object string 2", func: f };
    var obj3 = { myString: "object string 3", func: f2 };
    
    var bindFunc1 = f.bind ( obj1 );
    var bindFunc2 = f2.bind ( obj2 );
    
    var arrowFunc1 = f.apply ( obj1 );           // output: ?
    var arrowFunc2 = f2.apply ( obj2 );          // output: ?

    console.log ( obj1.myString );               // output: ?
    console.log ( obj2.myString );               // output: ?

    console.log ( arrowFunc1 ( ) );              // output: ?
    console.log ( arrowFunc2 ( ) );              // output: ?

    var newObj1 = new arrowFunc1 ( );            // error, why?

    var newObj2 = new bindFunc1 ( );             // output: ?
    var newObj3 = new bindFunc2 ( );             // error, why?

    console.log ( arrowFunc1.apply ( obj3 ) )    // output: ?
    console.log ( arrowFunc2.apply ( newObj2 ) ) // output: ?

    var result1 = bindFunc1 ( );                 // output: ?
    var result2 = bindFunc1.call ( newObj2 )     // output: ?
    
    var result3 = bindFunc2 ( );                 // output: ?
    var result4 = bindFunc2.call ( obj3 )        // output: ?
    
    console.log ( result1 ( ) )                  // output: ?
    console.log ( result2 ( ) )                  // output: ?

    console.log ( result3 ( ) )                  // output: ?
    console.log ( result4 ( ) )                  // output: ?

    console.log ( myString );                    // output: ? where does it defined?
    ```
    
## Determining `this` [ref](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md#determining-this)

Now, we can summarize the rules for determining `this` from a function call's call-site, in their order of precedence. Ask these questions in this order, and stop when the first rule applies.

1. Is the function called with `new` (**new binding**)? If so, `this` is the newly constructed object.

    `var bar = new foo()`

2. Is the function called with `call` or `apply` (**explicit binding**), even hidden inside a `bind` *hard binding*? If so, `this` is the explicitly specified object.

    `var bar = foo.call( obj2 )`

3. Is the function called with a context (**implicit binding**), otherwise known as an owning or containing object? If so, `this` is *that* context object.

    `var bar = obj1.foo()`

4. Otherwise, default the `this` (**default binding**). If in `strict mode`, pick `undefined`, otherwise pick the `global` object.

    `var bar = foo()`

That's it. That's *all it takes* to understand the rules of `this` binding for normal function calls. Well... almost.


## 參考資料
* This (1): https://ithelp.ithome.com.tw/articles/10196074
* This (2): https://ithelp.ithome.com.tw/articles/10196088
* This Priority: https://ithelp.ithome.com.tw/articles/10196282
* this: https://ithelp.ithome.com.tw/articles/10204472
* You Don't Know JS: https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch2.md