js 中 this 的詳細解釋

this 是js 中很重要的一個指針。可是每每也是最容易產生bug 的地方。一塊兒學習如下《你不知道的JavaScript》中關於this的介紹;app

調用棧 和 調用位置

每一個函數的this是在調用時被綁定的,徹底取決於函數的調用位置(也就是函數的調用方法)。

在理解this的綁定過程以前,首先要理解調用位置和調用棧;由於 調用位置 就在 當前正在執行的函數前一個調用 中。函數

什麼是調用棧和調用位置
調用位置:調用位置就是函數在代碼中被調用的位置(而不是聲明的位置)。
調用棧:就是爲了到達當前執行位置所調用的全部函數。oop

function baz() {
    // 當前調用棧是:baz
    // 所以, 當前調用位置是全局做用域
    console.log( "baz" );
    bar(); // <-- bar的調用位置
} 
function bar() {
    // 當前調用棧是baz -> bar
    // 所以, 當前調用位置在baz中
    console.log( "bar" );
    foo(); // <-- foo的調用位置
} 
function foo() {
    // 當前調用棧是baz -> bar -> foo
    // 所以, 當前調用位置在bar中
    console.log( "foo" );
} 
baz(); // <-- baz的調用位置

this綁定規則1:默認模式

分析代碼:學習

function foo() {
    console.log( this.a );
    //this經過默認綁定指向全局對象
} 
var a = 2;
foo(); // 2

this

function foo() {
    "use strict";
    console.log( this.a ); 
    //嚴格模式(strict mode)全局對象將沒法使用默認綁定
} 
var a = 2;
foo(); // TypeError: this is undefined

解釋:編碼

  1. 能夠把這條規則看做是沒法應用其餘規則時的默認規則。
  2. 當調用foo()時,this.a被解析成了全局變量a。爲何?由於在本例中,函數調用時應用了this的默認綁定,所以this指向全局對象。
  3. foo()是直接使用不帶任何修飾的函數引用進行調用的,所以只能使用默認綁定,沒法應用其餘規則。
  4. 若是使用嚴格模式(strict mode),那麼全局對象將沒法使用默認綁定,所以this會綁定到undefined

this綁定規則2:隱式綁定

分析代碼:指針

function foo() {
    console.log( this.a );
}
 var obj = {
    a: 2,
    foo: foo
};
obj.foo(); // 2

解析:code

  1. 不管是直接在obj中定義仍是先定義再添加爲引用屬性,foo嚴格來講都不屬於obj對象。
  2. 調用位置會使用obj上下文來引用函數,所以你能夠說函數被調用時obj對象「擁有」或者「包含」它。
  3. 當函數引用有上下文對象時,隱式綁定規則會把函數調用中的this綁定到這個上下文對象。
  4. 對象屬性引用鏈中只有最接近(包含層)會影響調用位置。

隱式丟失對象

function foo() {
    console.log( this.a );
} 
var obj = {
    a: 2,
    foo: foo
};
var bar = obj.foo; // 函數別名!
var a = "oops, global"; // a是全局對象的屬性

if(bar === obj.foo){
    console.log('true'); //true
    console.log(typeof bar) //function
    console.log(typeof obj.foo)  //function
}else {
    console.log('false')
} 

bar(); // "oops, global"
obj.foo();// 2

//等價的兩個函數,輸出不同的結果。爲何?
//其實var bar === window.bar; 它隱式綁定了window 對象,因此它訪問到的天然是 window.a

解析:雖然bar是obj.foo的一個引用,可是實際上,它引用的是foo函數自己,所以此時的bar()實際上是一
個不帶任何修飾的函數調用,所以應用了默認綁定。這種狀況是不能夠預料的。ip

還有一種 this 丟失=》 回調函數

回調函數丟失this綁定是很是常見的

function foo() {
console.log( this.a );
}
function doFoo(fn) {
    // fn其實引用的是foo
    fn(); // <-- 調用位置!
} 
var obj = {
    a: 2,
    foo: foo
};
var a = "oops, global"; // a是全局對象的屬性
doFoo( obj.foo ); // "oops, global"

this綁定規則3:顯式綁定

分析代碼:

function foo() {
console.log( this.a );
} 
var obj = {
    a:2
};
foo.call( obj ); // 2

經過foo.call(..),咱們能夠在調用foo時強制把它的this綁定到obj上

硬綁定

function foo(something) {
    console.log( this.a, something );
    return this.a + something;
} 
var obj = {
    a:2
};
var bar = foo.bind( obj );
var b = bar( 3 ); // 2 3
console.log( b ); // 5

bind(..)會返回一個硬編碼的新函數,它會把參數設置爲this的上下文並調用原始函數。

this綁定規則4:new綁定

JavaScript,構造函數只是一些使用new操做符時被調用的函數。它們並不會屬於某個類,也不會實例化一個類。實際上,它們甚至都不能說是一種特殊的函數類型,它們只是被new操做符調用的普通函數而已。

使用new來調用函數時的行爲

  1. 建立(或者說構造)一個全新的對象
  2. 新對象會被執行[[原型]]鏈接。
  3. 這個新對象會綁定到函數調用的this。
  4. 若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象。
function foo(a) {
    this.a = a;
} 
//new對普通函數的構造調用,默認行爲:建立一個新對象,得到對this的全部引用,返回新對象。
var bar = new foo(2);
console.log( bar.a ); // 2

//以上函數 至關於下面的函數

function foo(a){
    var obj = {};
    obj.a = a;
    reture obj
}
var bar = foo(2);
console.log( bar.a ); // 2

解析:使用new來調用foo(..)時,咱們會構造一個新對象並把它綁定到foo(..)調用中的this上。

綁定優先級

顯式綁定 > 隱式綁定
bind硬綁定 > new綁定 > 隱式綁定

判斷步驟

  1. 函數是否在new中調用(new綁定)?若是是的話this綁定的是新建立的對象。

    var bar = new foo()
  2. 函數是否經過call、apply(顯式綁定)或者硬綁定調用?若是是的話,this綁定的是指定的對象。

    function foo(a) {
        this.a = a;
    }
    var obj = {
        a:2
    }
    foo.call(obj,3); 
    //由於強制改變了foo 的上下文,this 顯示綁定 爲 obj;因此改變的是obj.a 的值 
    console.log(obj.a); //3
  3. 函數是否在某個上下文對象中調用(隱式綁定)?若是是的話,this綁定的是那個上下文對象。

    var bar = obj1.foo()
  4. 若是都不是的話,使用默認綁定。若是在嚴格模式下,就綁定到undefined,不然綁定到全局對象。

    var bar = foo()

    在默認綁定中容易被混淆的是 IIFE 自執行函數的使用

    var a = 3;
    var obj = {
        a:3,
        b:(function(){
            console.log(this.a)
        })(),
        c:function(){
            (function(){
                console.log(this.a)
            })()
        }
    }
    obj.c();
    
      //3
      //3
    
      this 都指向了window
相關文章
相關標籤/搜索