1. 程式人生 > >詳解javascript中的this的指向問題

詳解javascript中的this的指向問題

首先,要明白this 既不指向函式自身,也不指函式的詞法作用域。this一般存在於函式中,表示當前函式的執行上下文,如果函式沒有執行,那麼this沒有內容,只有函式在執行後this才有繫結。

然後,我們來看看this四種繫結規則,也可以說在四種不同函式執行方式時this的指向。

1.預設繫結(執行)

預設執行:即沒有其他繫結規則存在時的預設規則。這也是函式呼叫中最常用的規則。this指向window,嚴格模式下指向undefined

function fn(){
        console.log(this)
    }
    fn()

fn()列印到控制檯結果為window

開啟嚴格模式後

 function fn(){
        "use strict"
        console.log(this)
    }
    fn()

fn()列印到控制檯結果為undefined

2.隱式繫結(執行)

通過物件執行(通過上下文物件執行) obj.fn():當前的執行物件

function fn(){
            console.log(this.a)
        }
        var a = 10;
        var obj = {
            a:20,
            b:fn
        }
        var obj2 = {
            a:30,
            b:obj.b
        }
        obj2.b();    //??

obj2.b(); 列印到控制檯結果為30

因為obj2.b();在上述程式碼中等價於obj2.fn(),所以此時this指向obj2,所以列印結果為30;

多層呼叫

  function fn(){
            console.log(this.a)
        }
        var a = 10;
        var obj = {
            a:20,
            b:fn
        }
        var obj2 = {
            a:30,
            b:obj.b
        }
        var obj3 = {
            a:40,
            b:obj2
        }
        obj3.b.b()    //?

obj3.b.b() 列印結果為30

上述程式碼呼叫結果為obj3.b->obj2,obj2.b->obj.b->fn,因為this只獲取最近一層呼叫的上下文物件,即obj2

所以結果為30

隱式丟失(函式別名)

注意:這裡存在一個陷阱,大家在分析呼叫過程時,要特別小心

function fn() { 
    console.log( this.a );
}

var a = 2;

var obj = { 
    a: 3,
    b: fn 
};

var bar = obj.b;
bar(); //?

bar() 列印結果為2

為什麼會這樣,obj.b 賦值給bar,那呼叫bar()為什麼沒有觸發隱式繫結,使用的是預設繫結呢。

這裡有個概念要理解清楚,obj.b 是引用屬性,賦值給bar的實際上就是fn函式(即:bar指向fn本身)。

那麼,實際的呼叫關係是:通過bar找到fn函式,進行呼叫。整個呼叫過程並沒有obj的引數,所以是預設繫結,全域性屬性a。

隱士丟失(回撥函式)

function fn(){
            console.log(this.a)
        }
        var a=20;
        var obj = {
            a:20,
            b:fn
        }
setTimeout(obj.b, 2000);
function setTimeout(cb,t){
            // t
            cb()     //?
}

cb()列印結果為20

同樣的道理,雖然參傳是obj.fn,因為是引用關係,所以傳參實際上傳的就是fn物件本身的引用。對於setTimeout的呼叫,還是 setTimeout -> 獲取引數中fn的引用引數 -> 執行 fn 函式,中間沒有obj的參與。這裡依舊進行的是預設繫結。

3.顯示繫結(執行)

 function fn(){
            console.log(this.a)
        }

       var obj1= {
            a:10
        }
       var  obj2 ={
            a:20
        }
       var obj3 ={
            a:30
        }
      fn.bind(obj1)();
      fn.call(obj2);
      fn.apply(obj3);

fn.bind(obj1)();列印結果為10

fn.call(obj2); 列印結果為20

fn.apply(obj3);列印結果為30

這裡要注意fn.bind(obj1)後面還要加一個括號才執行!

4.new 繫結(建構函式執行(通過new執行))

js中的new操作符,和其他語言中(如JAVA)的new機制是不一樣的。js中,它就是一個普通函式呼叫,只是被new修飾了而已。

使用new來呼叫函式,會自動執行如下操作:

如果函式沒有返回其他物件,那麼new表示式中的函式呼叫會自動返回這個新物件。
從這點可以看出,this指向的就是物件本身。

function foo(a) { 
    this.a = a;
}

var a = 2;

var bar1 = new foo(3);
console.log(bar1.a); // ?

var bar2 = new foo(4);
console.log(bar2.a); // ?

因為每次呼叫生成的是全新的物件,該物件又會自動繫結到this上,所以列印結果分別為3,4

繫結規則優先順序

優先順序是這樣的,以按照下面的順序來進行判斷:
數是否在new中呼叫(new繫結)?如果是的話this繫結的是新建立的物件。
數是否通過call、apply(顯式繫結)或者硬繫結呼叫?如果是的話,this繫結的是 指定的物件。
數是否在某個上下文物件中呼叫(隱式繫結)?如果是的話,this繫結的是那個上下文物件。
果都不是的話,使用預設繫結。如果在嚴格模式下,就繫結到undefined,否則繫結到 全域性物件