關于this的二三事

什么是this

this是JS中一個非常重要的關鍵字。this 就是你 call 一個函數時,傳入的 context。任何形式的函數調用都可以寫成call形式,傳入的第一個參數就是this。

f(a,b)   等價于  f.call(undefined , a , b)
f.m.n(a,b)  等價于  f.m.n.call(f.m , a , b)
fn.call(context , p1 , p2)

所以當我們無法確定this時我們可以轉換成call形式來確認this。

如何確定this

通常我們確定this會用以下方式

1、console.log(this)
2、查看API源代碼
3、查看API相關文檔

this的使用

全局變量

function f() {
  console.log(this); // window
}
var a=1
function f() {
  console.log(this.a); 
}
f()
//1

對象的方法

當A對象的方法被賦予B對象,該方法中的this就從指向A對象變成了指向B對象。所以要特別小心,將某個對象的方法賦值給另一個對象,會改變this的指向。

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

但是,只有這一種用法(直接在obj對象上調用foo方法),this指向obj;其他用法時,this都指向代碼塊當前所在對象(瀏覽器為window對象)。

(obj.foo = obj.foo)()
/ 等價于 /
(obj.foo = function () {
  console.log(this);
})()
//window

可以這樣理解,obj和obj.foo儲存在兩個內存地址,簡稱為M1和M2。只有obj.foo()這樣調用時,是從M1調用M2,因此this指向obj。但是,上面情況是直接取出M2進行運算,然后就在全局環境執行運算結果(還是M2),因此this指向全局環境。

避免多層this

由于this的指向是不確定的,所以切勿在函數中包含多層的this。

var o = {
  f1: function () {
    console.log(this);
    var f2 = function () {
      console.log(this);
    }();
  }
}
o.f1()
// Object
// Window

上面代碼等價于

var temp = function () {
  console.log(this);
};
var o = {
  f1: function () {
    console.log(this);
    var f2 = temp();
  }
}

當我們把代碼修改為

var o = {
  f1: function() {
    console.log(this);
    var that = this;
    var f2 = function() {
      console.log(that);
    }();
  }
}
o.f1()
// Object
// Object

上面代碼定義了變量that,固定指向外層的this,然后在內層使用that,就不會發生this指向的改變。

避免數組處理方法中的this

數組的map和foreach方法,允許提供一個函數作為參數。這個函數內部不應該使用this。

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    });
  }
}
o.f()
// undefined a1
// undefined a2

上面代碼相當于

var temp = function(){
   o.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    });
} //this指向全局變量
var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: temp() 
}
o.f()
// undefined a1
// undefined a2

我們同樣可以這樣修改

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    var that = this;
    this.p.forEach(function (item) {
      console.log(that.v+' '+item);
    });
  }
}
o.f()
// hello a1
// hello a2

或者將this當作foreach方法的第二個參數,固定它的運行環境。

var o = {
  v: 'hello',
  p: [ 'a1', 'a2' ],
  f: function f() {
    this.p.forEach(function (item) {
      console.log(this.v + ' ' + item);
    }, this);
  }
}
o.f()
// hello a1
// hello a2

bind方法

bind方法用于將函數體內的this綁定到某個對象,然后返回一個新函數。

var counter = {
  count: 0,
  inc: function () {
    this.count++;
  }
};
var func = counter.inc;
func();
counter.count // 0
count // NaN

上面代碼中,函數func是在全局環境中運行的,這時inc內部的this指向頂層對象window,所以counter.count是不會變的,反而創建了一個全局變量count。因為window.count原來等于undefined,進行遞增運算后undefined++就等于NaN。

var func = counter.inc.bind(counter);
func();
counter.count
//1

使用bind方法將inc方法綁定到counter以后,再運行func就會得到正確結果。

小結

對于this,我們只要記得其本質就是call函數時傳入的第一個參數,如果函數調用形式不是 call 形式,請將其轉換為 call 形式,函數的this值可以用call改變,也可以用bind改變默認的this值。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 官方中文版原文鏈接 感謝社區中各位的大力支持,譯者再次奉上一點點福利:阿里云產品券,享受所有官網優惠,并抽取幸運大...
    HetfieldJoe閱讀 6,931評論 15 54
  • 與其他語言相比,函數的this關鍵字在JavaScript中的表現略有不同,此外,在嚴格模式和非嚴格模式之間也會有...
    codingC閱讀 586評論 0 0
  • 特別說明,為便于查閱,文章轉自https://github.com/getify/You-Dont-Know-JS...
    殺破狼real閱讀 712評論 0 1
  • 第5章 引用類型(返回首頁) 本章內容 使用對象 創建并操作數組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,268評論 0 4
  • 窗外疾馳的車輛 驅趕著深秋的冷霧 汽笛聲陣陣催促我忙碌 塵世那漸行漸遠的腳步 詩歌在柴米油鹽中荒蕪 撥開雜草叢生阡...
    古月ypc閱讀 195評論 0 0