JS 中 this 的指向

爲何要使用this? 解決的問題?

能夠先經過一個例子瞭解下javascript

function speak(){
    var name = this.name
    console.log("Hello I am --" + name)
}
var me = {
    name: 'a',
    speak: speak
}
var you = {
    name: 'b',
    speak: speak
}
me.speak()  //Hello I am -- a
you.speak()  //Hello I am -- b
複製代碼

this能夠在同一個執行環境中使用不一樣的上下文對象。它其實提供了一種更加優雅的方式來隱式「傳遞」一個對象引用,所以可使API設計的更加簡潔且易於複用。java

定義:this的指向在函數定義的時候是肯定不了的,只有函數執行的時候才能肯定this到底指向誰,實際上this的最終指向的是那個調用它的對象

其實關於this的指向問題能夠從函數的執行調用過程當中理解,當一個函數被調用時,會建立一個活動記錄(執行上下文)。這個記錄會包含函數在哪裏被調用、函數的調用方法、傳入的參數等信息,this也是這裏的一個屬性。當函數被某個對象調用時能夠理解爲在函數調用的那一刻它被調用對象擁有。因此this指向調用其的對象。windows

對於this的指向問題,通常都是根據以上的解釋去理解,雖然在通常的狀況下這樣的理解是不會有問題的,可是當在某些場景下這樣的解釋並非很準確,因此會讓人感受一直琢磨不透的感受。今天能夠就不一樣狀況展開討論下this指向的問題。看看下面的打印結果會是什麼數組

// 例子1
function test(){
    var a = 10;
    console.log(this.a);
    console.log(this);
}
test();
// 例子二
var b = 10;
var o = {
    b: 20,
    fn:function(){
        console.log(this.b); 
    }
}
o.fn();
複製代碼

按照上面的定義this最終指向的是調用它的對象,這裏的函數test實際是被Window對象所點出來的。因此例子1中的this指向的是windows。在例子2中函數的執行是經過o.fn()調用的,因此this的指向的固然是對象o了。這兩個例子能夠驗證上面的定義,可是仍是不夠準確的。markdown

// 例子三
var b = 10;
var o = {
    b: 20,
    fn:function(){
        console.log(this.b); 
    }
}
window.o.fn();
複製代碼

此時打印的應該是什麼呢?這段代碼和上面的那段代碼幾乎是同樣的,可是這裏的this爲何不是指向window,若是按照上面的理論,最終this指向的是調用它的對象,在此處就顯得不是很準確了。因此關於函數中this的指向實際上是能夠分爲三種狀況的app

  • 若是一個函數中有this,可是它沒有被上一級的對象所調用,那麼this指向的就是window
  • 若是一個函數中有this,這個函數有被上一級的對象所調用,那麼this指向的就是上一級的對象。
  • 若是一個函數中有this,這個函數中包含多個對象,儘管這個函數是被最外層的對象所調用,this指向的也只是它上一級的對象,這就能夠解釋例子三中的this不是指向window了

那麼如下的打印結果又應該是什麼?

// 例子4
var o = {
    a:10,
    b:{
        a:12,
        fn:function(){
            console.log(this.a); 
            console.log(this);
        }
    }
}
var j = o.b.fn;
j();
複製代碼

此處的this指向的是window,其實這裏只須要理解清楚一句話"this永遠指向的是最後調用它的對象",也就是看它執行的時候是誰調用的,例子4中雖然函數fn是被對象b所引用,可是在將fn賦值給變量j的時候並無執行因此最終指向的是window,這和例子3是不同的,例子3是直接執行了fn。因此說,那例子5中的打印結果又應該是什麼呢?函數

// 例子5
function foo() { 
    console.log( this.a );
}
var obj = { 
    a: 2,
    foo: foo 
};
var bar = obj.foo; // 函數別名!
var a = "xxxxx"
bar();
複製代碼

不一樣狀況下this的使用

1.構造函數版thisthis

// 例子6
function Fn(){
    this.user = "lh";
}
var a = new Fn();
console.log(a.user);
複製代碼

這裏之因此對象a能夠點出函數Fn裏面的user是由於new關鍵字能夠改變this的指向,將這個this指向對象a,咱們這裏用變量a建立了一個Fn的實例(至關於複製了一份Fn到對象a裏面),此時僅僅只是建立,並無執行,而調用這個函數Fn的是對象a,那麼this指向的天然是對象a,那麼爲何對象Fn中會有user,由於已經複製了一份Fn函數到對象a中,用了new關鍵字就等同於複製了一份。spa

!當this趕上return時設計

// 例子7
function fn() {  
  this.user = 'lh';  
  return {};  
}
var a = new fn;  
console.log(a.user);

// 例子8
function fn() {  
  this.user = 'lh';  
  return function(){};
}
var a = new fn;  
  console.log(a.user)

// 例子9
function fn() {  
  this.user = 'lh';  
  return 1;
}
var a = new fn;  
console.log(a.user);
複製代碼

由上可知,若是返回值是一個對象,那麼this指向的就是那個返回的對象,若是返回值不是一個對象那麼this仍是指向函數的實例。

2.箭頭函數中的this

var x=11;
var obj={
 x:22,
 say:()=>{
   console.log(this.x);
 }
}
obj.say();
複製代碼

箭頭函數不是經過function關鍵字定義的,也就不遵循以上的this規則,而是「繼承」外層做用域中的this指向。箭頭函數中的this是在定義函數的時候綁定,而不是在執行函數的時候綁定。所謂的定義時候綁定,就是this是繼承自父執行上下文!!中的this,好比這裏的箭頭函數中的this.x,箭頭函數自己與say平級以key:value的形式,也就是箭頭函數自己所在的對象爲obj,而obj的父執行上下文就是window,所以這裏的this.x實際上表示的是window.x,所以輸出的是11。

改變this指向的幾種方法(apply,call, bind)

var a = {
    user:"lh",
    fn:function(){
        console.log(this.user);
    }
}
var b = a.fn;
b();
複製代碼
  • 此時要改變this的指向,能夠經過call方法
var a = {
  user:"lh",
  fn:function(){
    console.log(this.user);
   }
}
var b = a.fn;
b.call(a);
複製代碼

經過在call方法,給第一個參數添加要把b添加到哪一個環境中,也就是說,this就會指向那個對象。 call方法除了第一個參數之外還能夠添加多個參數,以下

var a = {
  user:"lh",
  fn:function(p1, p2){
    console.log(this.user)
    console.log(p1+p2)
   }
}
var b = a.fn;
b.call(a, 1, 3複製代碼
  • 使用apply()方法,改變this指向的效果和call類似,在於他們傳遞的參數格式不一樣,第二個參數必須是一個數組,以下:
var a = {
  user:"lh",
  fn:function(p1, p2){
    console.log(this.user)
    console.log(p1+p2)
   }
}
var b = a.fn;
b.apply(a, [1, 3])
複製代碼

!當call和apply的第一個參數寫的是null,那麼this指向的是window對象

  • bind()方法

bind()方法也可用來改變this的指向,可是和call,apply方法在用法上有區別,以下:

var a = {
    user:"lh",
    fn:function(){
        console.log(this.user);
    }
}
var b = a.fn;
b.bind(a);
複製代碼

此時發現並不打印任何的結果,這是由於bind方法返回的是一個修改事後的函數,此時執行conole.log(b.bind(a))會獲得的結果是:

ƒ () {
  console.log(this.user);
}
複製代碼

call和apply都是改變上下文中的this並當即執行這個函數,bind方法可讓對應的函數想何時調就何時調用,而且能夠將參數在執行的時候添加,這是它們的區別,根據本身的實際狀況來選擇使用。而且也是能夠像call和apply同樣傳遞參數

相關文章
相關標籤/搜索