JavaScript初學者必看「this」

譯者按: JavaScript的this和Java等面嚮對象語言中的this大不同,bind()call()apply()函數更是將this的靈活度進一步延伸。javascript

原文: JavaScript: The Keyword ‘This’ for Beginnersjava

譯者: Fundebug小程序

爲了保證可讀性,本文采用意譯而非直譯。另外,本文版權歸原做者全部,翻譯僅用於學習。微信小程序

若是對JavaScript的關鍵字this理解不夠深入,有時候會掉入意想不到的坑。在這裏咱們總結了5條通用規則來幫助你判斷this到底指向什麼。雖然沒有囊括全部的狀況,但平常大部分狀況均可以使用這些規則來正確推斷。數組

  1. this的值一般是由所在函數的執行環境決定,也就是說要看函數是如何被調用的;
  2. 同一個函數每一次調用,this均可能指向不一樣的對象;

全局對象 (Global Object)

打開Chrome瀏覽器開發者面板(Windows: Ctrl + Shift + J)(Mac: Cmd + Option + J),而且輸入:瀏覽器

console.log(this);

看看輸出了什麼? 微信

// Window {}

window對象! 由於在全局做用域下,this指向全局對象。在瀏覽器中全局對象就是window對象。
爲了讓你更加清楚理解爲何this會指向window對象,咱們來看另一個例子:app

var myName = 'Brandon';

咱們能夠經過在控制檯輸入myName來訪問其值:函數

myName
// 輸出 'Brandon'


其實,全部在全局定義的變量都綁定到window對象。咱們來作以下測試: 學習

window.myName
// 輸出 'Brandon'
window.myName === myName
// 輸出 true


如今咱們將this放到函數內部,看看有何效果。 

function test(){
  return this;
}
test();


聲明的對象 (Declared Object)

你會發現this依然指向全局的window對象。由於this關鍵字沒有處於一個聲明的對象內部,默認指向全局window對象。這一點可能對於大部分初學者來講有點難以理解。當讀完這篇文章,你就會豁然開朗。
注意:若是在strcit模式下,上面的例子中thisundefined

this關鍵字在一個聲明對象內部使用,其值會被綁定到調用該this的函數的最近的父對象。咱們用例子來講明這個問題:

var person = {
  first: 'John',
  last: 'Smith',  
  full: function() {
    console.log(this.first + ' ' + this.last);
  }
};
person.full();
// 輸出 'John Smith'


在被聲明的對象person中的full函數裏面使用了this, 那麼調用thisfull函數的最近的父對象就是person, 所以,this指向person。 

爲了更好的描述this其實是指向person對象的,你能夠拷貝以下代碼到瀏覽器控制檯,將this打印出來。

var person = {
  first: 'John',
  last: 'Smith',  
  full: function() {
    console.log(this);
  }
};
person.full();
// 輸出 Object {first: "John", last: "Smith", full: function}

咱們接下來看一個更復雜的例子:

var person = {
  first: 'John',
  last: 'Smith',
  full: function() {
    console.log(this.first + ' ' + this.last);
  },
  personTwo: {
    first: 'Allison',
    last: 'Jones',
    full: function() {
      console.log(this.first + ' ' + this.last);
    }
  }
};

在這裏咱們有嵌套的對象,在這個時候,this分別指向誰呢? 咱們將其打印出來看一看:

person.full();
// 輸出 'John Smith'
person.personTwo.full();
// 輸出 'Allison Jones'

你會發現知足咱們前面所描述的規則:其值會被綁定到調用this的函數的最近的父對象。 

new關鍵字

當使用new關鍵字構建一個新的對象,this會綁定到這個新對象。咱們來看一個例子:

function Car(make, model) {
  this.make = make;
  this.model = model;
};

根據第一條規則,你可能會推斷this指向全局對象。可是若是咱們使用new關鍵字來聲明一個新的變量,Car函數中的this將會綁定一個新的空對象,而後初始化this.makethis.model的值。

var myCar = new Car('Ford', 'Escape');
console.log(myCar);
// 輸出 Car {make: "Ford", model: "Escape"}

callbind, 和apply

咱們能夠顯示的在call()bind()apply()中設置this的綁定對象。這三個函數很相似,可是咱們須要注意它們微小的區別。

咱們來看一個例子:

function add(c, d) {
  console.log(this.a + this.b + c + d);
}
add(3,4);
// 輸出 NaN


add
函數輸出NaN, 由於this.athis.b未定義。 

如今咱們引入對象,而且使用call()apply()來調用:

function add(c, d) {
  console.log(this.a + this.b + c + d);
}
var ten = {a: 1, b: 2};
add.call(ten, 3, 4);
// 輸出 10
add.apply(ten, [3,4]);
// 輸出 10

當咱們使用add.call(),第一個參數是this須要綁定的對象,剩下的是add函數原本的參數。 

所以,this.a指向ten.a,this.b指向ten.badd.apply()也相似,除了第二個參數是一個數組,用於存儲add函數的參數。

bind()函數和call()相似,可是bind()函數不會當即被調用。bind()函數會返回一個函數,而且將this綁定好。接下來咱們來用例子來幫助理解bind()函數的應用場景:

var small = {
  a: 1,
  go: function(b,c,d){
    console.log(this.a+b+c+d);
  }
}
var large = {
  a: 100
}

執行: 

small.go(2, 3, 4);
// 輸出 10


若是咱們想使用large.a的值,而不是small.a呢? 咱們可使用call/apply

small.go(2, 3, 4);
// 輸出 10

可是,若是咱們如今還不知道這三個參數應該傳入什麼值,應該怎麼辦呢? 咱們可使用bind

var bindTest = small.go.bind(large, 2);


若是咱們將bindTest在控制檯下打印出來,咱們會看到: 

console.log(bindTest);
// 輸出 function (b,c,d){console.log(this.a+b+c+d);}

注意:該函數已經將this綁定到large對象,而且傳入了第一個參數b。因此,咱們接下來是須要傳入餘下的參數便可: 

bindTest(3, 4);
// 輸出 109


箭頭函數(=>)
 

由於須要很大的篇幅,咱們會專門寫一篇博客來介紹。

結論

當你讀完這篇博客,你應該能夠理解大多數狀況下this指向的對象。
接下來咱們來總結一下:

  1. this的值一般是由當前函數的執行環境所決定;
  2. 在全局做用域,this指向全局對象 (window對象);
  3. 當使用new關鍵字聲明,this指向新建對象;
  4. 咱們可使用call()bind()apply()來設置this
  5. 箭頭函數不會綁定this

關於Fundebug

Fundebug專一於JavaScript、微信小程序、微信小遊戲、支付寶小程序、React Native、Node.js和Java實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了6億+錯誤事件,獲得了Google、360、金山軟件等衆多知名用戶的承認。歡迎免費試用!

版權聲明:

轉載時請註明做者Fundebug以及本文地址:

https://blog.fundebug.com/2017/05/17/javascript-this-for-beginners/

相關文章
相關標籤/搜索