JavaScript的this機制與箭頭函數(一)——this綁定的4種機制

傳聞JavaScript有兩座大山——this與作用域,在函數中,這兩個難點會交織在一起。本來this就一臉懵逼,又有同樣懵逼的作用域,所以說很多人就會
這裏寫圖片描述
es6中,又出現了一個箭頭函數,箭頭函數的this又比普通函數的this“骨骼驚奇”得多。欲搞清楚箭頭函數的this指向,必須要搞清楚this的四種綁定機制。
參考鏈接:深入理解this機制系列第一章

默認綁定

全局環境中,this默認綁定到window
在控制檯中分別執行以下代碼,注意,全局變量相當於window對象的屬性

<1> console.log(this==window) // true
<2> var a={
        foo:"123",
        bar:"456",
        that:this
    }
    a.that // window
<3> this // window

函數獨立調用或者被嵌套函數獨立調用時,this默認綁定到window
雖然test()函數被嵌套在obj.foo()函數中,但test()函數是獨立調用,而不是方法調用。所以this默認綁定到window

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

立即執行函數實際上是函數聲明後直接調用執行

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

閉包中被返回到外部的函數也是獨立調用

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

在這個例子中,如果test函數中console的是變量a,那麼按照閉包的運行原理輸出的是1,但這裏輸出的是this.a,this又綁定到了window,所以輸出的值是0

隱式綁定

函數做爲對象方法調用時(即方法調用),this隱式綁定到該直接對象

function foo(){
    console.log(this.a);
};
var obj1 = {
    a:1,
    foo:foo,
    obj2:{
        a:2,
        foo:foo
    }
}
//foo()函數的直接對象是obj1,this隱式綁定到obj1
obj1.foo();//1
//foo()函數的直接對象是obj2,this隱式綁定到obj2
obj1.obj2.foo();//2

隱式丟失
隱式丟失是指被隱式綁定的函數丟失綁定對象,從而默認綁定到window,有以下幾種情況會造成隱式丟失

<1>函數別名

var a = 0;
function foo(){
    console.log(this.a);
};
var obj = {
    a : 2,
    foo:foo
}
//把obj.foo賦予別名bar,造成了隱式丟失
var bar = obj.foo;
bar();//0

<2>參數傳遞

var a = 0;
function foo(){
    console.log(this.a);
};
function bar(fn){
    fn();
}
var obj = {
    a : 2,
    foo:foo
}
//把obj.foo當作參數傳遞給bar函數時,有隱式的函數賦值fn=obj.foo
bar(obj.foo);//0

<3>作爲內置函數的回調

var a = 0;
function foo(){
    console.log(this.a);
};
var obj = {
    a : 2,
    foo:foo
}
//如果沒有隱式丟失,this應該指向obj
setTimeout(obj.foo,100);//0
//結果爲0,證明函數作爲作爲內置函數的回調時發生了隱式丟失

<4>其他特殊情況

function foo() {
    console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
//將o.foo函數賦值給p.foo函數,然後立即執行。相當於僅僅是foo()函數的立即執行
(p.foo = o.foo)(); // 2

引用與調用的區別
調用:用原函數名調用函數,如foo()
引用:通過別名調用函數,比如var a=foo;a()

顯式綁定

通過call()、apply()、bind()方法把對象綁定到this上,叫做顯式綁定。對於被調用的函數來說,叫做間接調用

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

顯式綁定不能解決隱式丟失問題

function foo() {
    setTimeout(function (){
        console.log('id:',this.id);
    }, 100);
}
var id = 21;
foo.call({ id: 42 });//21

new綁定

構造函數通常不使用return關鍵字,它們通常初始化新對象,當構造函數的函數體執行完畢時,它會顯式返回。在這種情況下,構造函數調用表達式的計算結果就是這個新對象的值

function fn(){
    this.a = 2;
}
var test = new fn();
console.log(test);//{a:2}

如果構造函數使用return語句但沒有指定返回值,或者返回一個原始值,那麼這時將忽略返回值,同時使用這個新對象作爲調用結果

function fn(){
    this.a = 2;
    return;
}
var test = new fn();
console.log(test);//{a:2}

如果構造函數顯式地使用return語句返回一個對象,那麼調用表達式的值就是這個對象

var obj = {a:1};
function fn(){
    this.a = 2;
    return obj;
}
var test = new fn();
console.log(test);//{a:1}

以上便是this綁定的四種機制,下一篇博文將講解箭頭函數this的指向機制

發佈了66 篇原創文章 · 獲贊 47 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章