Javascript千面之變幻莫測的this指向

【前話】:轉發請事先聯繫,且不得應用於商業場景(筆者發現不打招呼被轉發,並用於賺錢,沒給我分潤?)javascript

相信不少前端人對「this」的指向是很懵逼的,由於this的指向老是變幻莫測,在不一樣的調用環境中,它的指向老是各不相同。前端

在面試中,this也是常常考的必考題之一,不少前端老鳥常常會在this這裏掉坑。java

接下來,看筆者來一層一層的揭開this指向的面紗。node

1.事件調用環境中的this指向

<div class="box1"></div>
<div class="box2"></div>

<script>
  const box1 = document.querySelector('.box1')
  const box2 = document.querySelector('.box2')

  function event() {
    console.log(this)
  }

  box1.onclick = event  ------>  <div class="box1"></div>
  box2.onclick = event  ------>  <div class="box2"></div>
  event()   ------>  window對象

</script>

從上述實例中能夠看出:面試

box1點擊後,event方法中this指向div.class.box1數組

box2點擊後,event方法中this指向div.class.box2瀏覽器

event()直接調用,this指向window對象,嚴格模式下指向的是undefinedapp

總結:1.當事件被動調用時,誰去觸發,this就指向誰; 2.當時間主動執行時(事件後面加()), this指向的就是window,嚴格模式下指向的是undefined函數

2.全局環境下的this指向

前端全局環境分兩部分,瀏覽器環境,node環境測試

瀏覽器全局環境

<script>
  var aaaa = 100000
  function aaatestEvent() {
    console.log(this)
  }
  // console.log(this) ------> window對象
  // aaatestEvent() === window.aaatestEvent() ------> window對象
  // aaa = window.aaa
</script>

我相信有不少小夥伴應該試過在script書寫的時候直接打印出this的指向,看看this指向的是個什麼東東?沒錯,指向的是window全局對象;咱們常常定義的常量和變量也存放在window對象中

node環境

console.log(this)  -----> {}
console.log(this === module.exports)  -----> true

小夥伴從上述實例中能夠看出,在node環境中,this指向的是暴露出來的對象

總結:全局環境中,瀏覽器環境指向的是window對象,node環境指向的是暴露出來的object對象

3.函數內部環境中的this指向

在實際開發中,調用函數方法時,爲了節約對內存的消耗,函數方法常常被存放於一個object對象中去,有利於方法的管理性和可讀性

實例1:

<script>
  var obj = {
    num: 100,
    fn: function () {
      console.log(this)
    }
  }
  // 調用
  obj.fn() ----> this指向obj
</script>

obj.fn()運行後,this指向的obj,能夠發現,函數方法存在對象中的時候,函數內部的this指向的調用它的對象

實例2:

<script>
  var obj = {
    num: 100,
    fn: function () {
      console.log(this)
    }
  }
  // 調用
  obj.fn() ----> this指向obj
  window.obj.fn() ---> this指向obj
</script>
var obj1 = {
  num: 100,
  aaa: {
    fn: function () {
      console.log(this)
    }
  }
}
// 調用
window.obj1.aaa.fn() ---> this指向aaa

經過實例1中的測試,咱們稍微總結了下this指向的是調用它的對象;

咱們在上面瀏覽器全局環境中講過,定義常量和變量會被存放在window對象中,經過window對象輸出,咱們也能發現obj在window中,能夠嘗試去執行

window.obj.fn(),咱們發現this仍是指向obj啊?筆者剛剛不是說this指向的是調用它的對象嗎?如今是window在調用obj的fn方法啊?怎麼回事?

window.obj1.aaa.fn(),咱們發現this指向了aaa啊?

解析:

    實例1:obj.fn()    fn被obj調用,this指向obj

    實例2:window.obj.fn()    fn被window調用,this指向obj

                  window.obj1.aaa.fn()    fn被window調用,this指向aaa

此時,咱們總結:

實例1:【this指向調用它的對象】

實例2:【函數被多層對象包含,函數被最外層對象調用時,this指向的是它的上一級的對象】

有不少面試過程當中,常常在這個地方會被問到,注意別掉坑哦?

有些考官特別可惡,喜歡在這個地方設置障礙故意繞懵面試者,請看下面的測試實例

<script>
  var obj = {
    num: 100,
    aaa: {
      fn: function () {
        console.log(this)
      }
    }
  };

  var abc = obj.aaa.fn;
  window.obj.aaa.fn()    ------> 指向aaa
  abc()      ------>指向window
  abc()等同於window.abc(),此處abc省略了window
</script>

解析上面實例

window.obj.aaa.fn()  就是上面總結的實例2,this指向的是它的上一級的對象

此時假如window.obj.aaa.fn被定義爲到了一個abc上,調用abc()就至關於調用window.abc(), 實例1的總結是這樣的:this指向調用它的對象,this的輸出執行是abc方法被執行了,誰調用了?沒錯window對象調用了,因此this指向的就是window對象,此處很是的繞,請小夥伴們仔細思考哦!

4.構造函數環境中的this指向

哈哈哈,凡事都是有例外的,當咱們在寫構造函數或者在寫面向對象的邏輯中的時候,this的指向狀況又不同了,請看實例:

不含返回值的構造函數

<script>
  function fn() {
    this.num = 10
    console.log(this)
  }

  var obj = new fn();
  console.log(obj)
  /*
  * new在此時的做用
  *
  * 1. 調用fn這個函數方法
  * 2. 自動建立一個object對象
  * 3. 把建立出來的對象與this進行綁定
  * 4。若是構造函數沒有返回值,隱式返回this對象
  * */
</script>

上面的例子能夠看出

this返回的東西和fn new出來的實例返回的東西是同樣的,這個就是new的魅力

    哈哈哈,扯遠了,主要是照顧一下剛入前端坑的小夥伴們,解釋下new所作的事情

    1. 調用fn這個函數方法

    2. 自動建立一個object對象

    3. 把建立出來的對象與this進行綁定

    4. 若是構造函數沒有返回值,隱式返回this對象

總結:構造函數中,this指向的是新建立出來的而且若是又屬性值,進行綁定屬性的新建對象

ok, ok,anyway!咱們再來個複雜的,同時也是更有意思的實例

<script>
  function fn() {
    this.num = 10
    console.log(this)
  }

  // 此處的num與fn中的num不是同一個東西哦
  fn.num = 20;
  // 每一個方法都有本身的原型鏈,在原型鏈上定義一個num
  fn.prototype.num = 30;
  // 在原型鏈上定義一個method的方法
  fn.prototype.method = function () {
    console.log(this);
  }

  var prototype = fn.prototype;   就至關於var prototype = {}
  var method = prototype.method

  // 直接new fn
  new fn()
  new fn().method()
  prototype.method()
  method()

</script>

實例解析:

    1.new fn()  剛剛說了,this指向的是新建立出來的而且若是又屬性值,進行綁定屬性的新建對象 ,因此,此時this輸出的值是{num: 10}

    2.new fn().method(), new的做用說了,先調用方法,而後建立一個對象,而後進行綁定this,最後輸出;
    new fn().method()就至關於{}.method(), 空對象{}綁定this下面的num = 10,因此此時this輸出的值是{num: 10}

    3.函數方法都有原型鏈,原型鏈就是一個實例化了空對象{}進行擴展,並綁定原型鏈上的屬性(能夠理解爲new了一個原型鏈)

    prototype.method()執行中,var prototype = fn.prototype 就至關於 var prototype = {},原型鏈上含有不少私有方法和定義屬性等等,

    好比咱們定義的fn.prototype.num = 30; fn.prototype.method = function () { console.log(this); }

    因此此時this輸出的值是{num: 30,......}

    4.method()就至關於window.method(),很顯然,全局環境中的this指向的是window, 全局window對象沒有定義num,因此若是輸出this.num就是undefined

這個實例很大程度依賴小夥伴們對js基礎知識掌握的考驗

總結:在沒有返回值的構造函數中,this指向的是new實例化建立出來的對象,對象中與函數this進行綁定(原型鏈相同原理,參考解析3)

有return返回值的構造函數

看實例

<script>
  function fn() {
    this.num = 10;
    return ''
    /*
    *  '' 空字符串 ---> 10
    *  [] 數組 --->undefined
    *  {} 對象 ---> 10
    *  123 數字 ---> 10
    *  function(){} 方法 ---> undefined
    *  null  ------> 10
    * */
  }

  var obj = new fn()
  console.log(obj.num);
</script>

從上述實例中,咱們能夠

總結:構造函數有返回值,當返回值是對象(type of進行類型查看)的時候,this指向的是實例化建立出來的對象(obj)

            當返回值不是對象的時候,保持本來規則不變,

            此處null是一個特例,返回null時,this指向的是實例化建立出來的對象(obj)

5.箭頭函數環境中的this指向 

常常有前端剛入門的小夥伴,this指向發生偏移的時候,有些同伴就說,趕忙用ES6的箭頭函數啊?這樣this的指向就不會變了,至於爲何不會變,本身都說不出來的狀況

接下來,讓咱們剖析一下箭頭箭頭函數

<script>
  var box1 = document.querySelector('.box1');
  var box2 = document.querySelector('.box2');

  box1.onclick = move1;
  box2.onclick = move2;

  function move1() {
    setTimeout(function () {
      console.log(this);
    }, 1000)
  }

  function move2() {
    setTimeout(() => {
      console.log(this);
    }, 1000)
  }
</script>

解析:爲什麼在setTimeout中使用function(){}和箭頭函數() => {}this的指向不同呢?

          延遲調用的函數在setTimeout中是以入參的形式調用的

        1.  其實若是傳入的入參提到外面進行定義,不就是以下面同樣嘛,這是不難理解,fn()就是至關於window.fn(),因此this指向window對象

function fn() {
  console.log(this)
}

function move1() {
  setTimeout(fn, 1000)
}

    2.入參以箭頭函數形式進行程序執行,this輸出爲調用的節點了,是否是以下面同樣了

<script>
  var obj = {
    fn: () => {
      console.log(this)
    }
  }
  // obj是不能造成獨立做用域的
  obj.fn()  ------> this指向的是window對象
</script>

總結:箭頭函數中的this指向官方文檔指的是上下文環境中的this;

           咱們理解爲:箭頭函數自己是沒有this和argument的,在箭頭函數中調用this實際上就是在調用定義在上一層做用域的this指向;這裏強調一下,指的是上一層做用域,由於對象是不能造成獨立的做用域的。

this的指向經常使用的幾種狀況都在這裏了,小夥伴們請認真閱讀,仔細思考才能不被繞暈哦,待看後期廣大借閱者理解程度,若是理解的不是很透徹,筆者能夠考慮錄個視頻也無不可哦…………^ - ^ 

6.修改this指向

關於修改this的指向的方法,能夠參考一下個人另外一篇博文:Javascript千面之call、apply、bind區別和使用 

若是你喜歡這篇文章,請給我點個贊吧~~
只看不點贊,等於耍流氓~

●﹏●

●﹏●

●﹏●

相關文章
相關標籤/搜索