詳解javascript中的this的指向問題

  • 2019 年 10 月 3 日
  • 筆記

首先,要明白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,否則綁定到 全局對象。

Tags: