javascript this探究

  關於javascript的this講解的文章已經多如牛毛了,我本人在開發過程中也用到過很多次。這次趁有空,整理一下關於this的一系列問題:包括this的指向,嚴格模式和非嚴格模式下的區別,什麼情況下可以改變this的指向以及在ES6下箭頭函數中this的指向。

一.嚴格模式下this的指向

嚴格模式下:

function aa(){
    'use strict';
     console.log(this)   //undefined
}
aa();  

非嚴格模式下:

function aa(){
    console.log(this)   //window
}
aa();  

二.this的指向

MDN中對this的解釋如下:

在絕大多數情況下,函數的調用方式決定了this的值。this不能在執行期間被賦值,並且在每次函數被調用時this的值也可能會不同。

  簡單點來說,this的指向在聲明時是未定義的,只有在它被調用時纔會被賦值,要搞清楚它到底指向什麼,只要搞清楚到底誰調用它即可。
  下面有幾種情況可以幫助我們加快理解this的指向問題:
1.第一種情況:

function aa(){
  console.log(this)   //window
}
aa();

此時是window對象調用了aa()函數,所以this指向window。

2.第二種情況

var test = {
  aa:"hello",  
  bb:function(){
    console.log(this.aa)  //"hello"
  }
}
test.bb();

此時是test對象調用了this,所以this此時指向的bb對象中的aa;

var test = {
  aa:"hello",  
  bb:function(){
    console.log(this)  //test (object)
  }
}
test.bb();

此時就很清晰的能看到this指向的test對象。

3.第三種情況

var test = {
  aa:"hello",  
  bb:{
    aa:'hello2',
    cc:function(){
        console.log(this.aa)  //"hello2"
     }
  }
}
test.bb.cc();

此時調用cc中this的對象是bb,而不是test,所以this指向的是bb。

以上三個例子已經比較清晰的解釋了this的指向問題:
誰最後調用了this,誰就是this指向的對象。

三.什麼情況下this的指向會被改變

這些情況大致可以分爲兩類:

1.關鍵字類。主要有new,return
2.函數類。 主要有call,apply,bind

第一類.關鍵字類
我們先看new的例子:

function aa(){
   this.name = "Tom"    
}

var bb = aa(); //bb is undefined;
var bb = new aa();
console.log(bb.name)  //"Tom"

new關鍵字的關鍵在於實例化了構造函數aa()。使它的函數能夠順利的拿到裏面的name。

return的例子:

function aa(){
   this.name = "Tom";
   return {};
}
var bb = new aa();
console.log(bb.name)  //undefined

當return的內容是一個對象時,則this會指向這個對象。

function aa(){
   this.name = "Tom";
   return 'aaa';
}
var bb = new aa();
console.log(bb.name)  //"Tom"

當return的內容是一個非對象時,則this的指向不會發生改變。

第二類.函數類

1.call()函數
老規矩,先關門放MDN。

call() 方法調用一個函數,其具有一個指定的this值和分別地提供的參數(參數的列表)。

簡單來說就是可以通過call()函數來指定你想調用的this所造的環境,同時還可以追加多個參數。
以下是MDN上的官方示例:

function Product(name, price) {
  this.name = name;
  this.price = price;
}

function Food(name, price) {
  Product.call(this, name, price);
  this.category = 'food';
}

console.log(new Food('cheese', 5).name);
// expected output: "cheese"

如果沒有調用call()函數,最後打印的應該是food,調用後纔會改成cheese。如果覺得上面的例子還不夠清晰,我們可以再看一個,如果覺得ok了,請跳過。

var aa = {
   name:"tom",
   bb:function(x, y){
        console.log(this.name);  //"tom"
        console.log(x,y);  // 1,2
   }
}

var b = aa.bb
b.call(aa);
b.call(aa,1,2)

2.apply函數

apply() 方法調用一個具有給定this值的函數,以及作爲一個數組(或類似數組對象)提供的參數。
該方法的作用和 apply()方法類似,只有一個區別,就是call()方法接受的是若干個參數的列表,而apply()方法接受的是一個包含多個參數的數組。

var aa = {
   name:"tom",
   bb:function(x, y){
        console.log(this.name);  //"tom"
        console.log(x,y);  // 1,2
   }
}
var b = aa.bb
b.apply(aa,[1,2])

3.bind函數

bind()方法創建一個新的函數, 當這個新函數被調用時其this置爲提供的值,其參數列表前幾項置爲創建時指定的參數序列。

var module = {
  x: 42,
  getX: function() {
    return this.x;
  }
}

var unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined

var boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42

需要注意的是,bind函數與apply和call都有所不同,不同之處在於bind不是立即執行的。

bind() 函數會創建一個新綁定函數,綁定函數與被調函數具有相同的函數體(在 ECMAScript 5 中)。

我們看一個例子:

var aa = {
    name:"tom",
    bb:function(){
        console.log(this.name)
    }
}

var cc = aa.bb;
var dd = cc.bind(aa);
console.log(dd);  //type function aa()
dd(); // "tom"

三.ES6箭頭函數中的this

箭頭函數與其他函數區別很大,我們直接看MDN:

箭頭函數表達式的語法比函數表達式更短,並且沒有自己的this,arguments,super或 new.target。

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this| 正確地指向person 對象
  }, 1000);
}

var p = new Person();

當然,通過相關函數也不能改變=>中的this指向。

通過 call 或 apply 調用
由於 箭頭函數沒有自己的this指針,通過 call() 或 apply() 方法調用一個函數時,只能傳遞參數(不能綁定this—譯者注),他們的第一個參數會被忽略。(這種現象對於bind方法同樣成立—譯者注)

總結一下,在箭頭函數中,是沒有它自己的this的,它只會從自己的作用域鏈的上一層繼承this。
我們來看例子:

var obj = {
    fn:()=>{
        console.log(this)  //window
    }
};
obj.fn();

再看一個:

function obj(){
  let aa = ()=>{
    console.log(this);
  }
  aa();
}

obj() // window
var bb = new obj(); //obj type object

第二個例子中,new obj()後之所以打印出了obj(),是因爲new關鍵字綁定了this到返回的新的對象object中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章