【進階3-2期】JavaScript深刻之從新認識箭頭函數的this

咱們知道this綁定規則一共有5種狀況:前端

  • 一、默認綁定(嚴格/非嚴格模式)
  • 二、隱式綁定
  • 三、顯式綁定
  • 四、new綁定
  • 五、箭頭函數綁定

其實大部分狀況下能夠用一句話來歸納,this老是指向調用該函數的對象webpack

可是對於箭頭函數並非這樣,是根據外層(函數或者全局)做用域(詞法做用域)來決定this。git

對於箭頭函數的this總結以下:github

  1. 箭頭函數不綁定this,箭頭函數中的this至關於普通變量。
  2. 箭頭函數的this尋值行爲與普通變量相同,在做用域中逐級尋找。
  3. 箭頭函數的this沒法經過bind,call,apply來直接修改(能夠間接修改)。
  4. 改變做用域中this的指向能夠改變箭頭函數的this。
  5. eg. function closure(){()=>{//code }},在此例中,咱們經過改變封包環境closure.bind(another)(),來改變箭頭函數this的指向。

題目1

/**
 * 非嚴格模式
 */

var name = 'window'

var person1 = {
  name: 'person1',
  show1: function () {
    console.log(this.name)
  },
  show2: () => console.log(this.name),
  show3: function () {
    return function () {
      console.log(this.name)
    }
  },
  show4: function () {
    return () => console.log(this.name)
  }
}
var person2 = { name: 'person2' }

person1.show1()
person1.show1.call(person2)

person1.show2()
person1.show2.call(person2)

person1.show3()()
person1.show3().call(person2)
person1.show3.call(person2)()

person1.show4()()
person1.show4().call(person2)
person1.show4.call(person2)()


web

面試

算法

跨域

瀏覽器



正確答案以下:安全

person1.show1() // person1,隱式綁定,this指向調用者 person1 
person1.show1.call(person2) // person2,顯式綁定,this指向 person2

person1.show2() // window,箭頭函數綁定,this指向外層做用域,即全局做用域
person1.show2.call(person2) // window,箭頭函數綁定,this指向外層做用域,即全局做用域

person1.show3()() // window,默認綁定,這是一個高階函數,調用者是window
                  // 相似於`var func = person1.show3()` 執行`func()`
person1.show3().call(person2) // person2,顯式綁定,this指向 person2
person1.show3.call(person2)() // window,默認綁定,調用者是window

person1.show4()() // person1,箭頭函數綁定,this指向外層做用域,即person1函數做用域
person1.show4().call(person2) // person1,箭頭函數綁定,
                              // this指向外層做用域,即person1函數做用域
person1.show4.call(person2)() // person2

最後一個person1.show4.call(person2)()有點複雜,咱們來一層一層的剝開。

  • 一、首先是var func1 = person1.show4.call(person2),這是顯式綁定,調用者是person2show4函數指向的是person2
  • 二、而後是func1(),箭頭函數綁定,this指向外層做用域,即person2函數做用域

首先要說明的是,箭頭函數綁定中,this指向外層做用域,並不必定是第一層,也不必定是第二層。

由於沒有自身的this,因此只能根據做用域鏈往上層查找,直到找到一個綁定了this的函數做用域,並指向調用該普通函數的對象。

題目2

此次經過構造函數來建立一個對象,並執行相同的4個show方法。

/**
 * 非嚴格模式
 */

var name = 'window'

function Person (name) {
  this.name = name;
  this.show1 = function () {
    console.log(this.name)
  }
  this.show2 = () => console.log(this.name)
  this.show3 = function () {
    return function () {
      console.log(this.name)
    }
  }
  this.show4 = function () {
    return () => console.log(this.name)
  }
}

var personA = new Person('personA')
var personB = new Person('personB')

personA.show1()
personA.show1.call(personB)

personA.show2()
personA.show2.call(personB)

personA.show3()()
personA.show3().call(personB)
personA.show3.call(personB)()

personA.show4()()
personA.show4().call(personB)
personA.show4.call(personB)()




正確答案以下:

personA.show1() // personA,隱式綁定,調用者是 personA
personA.show1.call(personB) // personB,顯式綁定,調用者是 personB

personA.show2() // personA,首先personA是new綁定,產生了新的構造函數做用域,
                // 而後是箭頭函數綁定,this指向外層做用域,即personA函數做用域
personA.show2.call(personB) // personA,同上

personA.show3()() // window,默認綁定,調用者是window
personA.show3().call(personB) // personB,顯式綁定,調用者是personB
personA.show3.call(personB)() // window,默認綁定,調用者是window

personA.show4()() // personA,箭頭函數綁定,this指向外層做用域,即personA函數做用域
personA.show4().call(personB) // personA,箭頭函數綁定,call並無改變外層做用域,
                              // this指向外層做用域,即personA函數做用域
personA.show4.call(personB)() // personB,解析同題目1,最後是箭頭函數綁定,
                              // this指向外層做用域,即改變後的person2函數做用域

題目一和題目二的區別在於題目二使用了new操做符。

使用 new 操做符調用構造函數,實際上會經歷一下4個步驟:

  1. 建立一個新對象;
  2. 將構造函數的做用域賦給新對象(所以this就指向了這個新對象);
  3. 執行構造函數中的代碼(爲這個新對象添加屬性);
  4. 返回新對象。

上期思考題解

依次給出console.log輸出的數值。

var num = 1;
var myObject = {
    num: 2,
    add: function() {
        this.num = 3;
        (function() {
            console.log(this.num);
            this.num = 4;
        })();
        console.log(this.num);
    },
    sub: function() {
        console.log(this.num)
    }
}
myObject.add();
console.log(myObject.num);
console.log(num);
var sub = myObject.sub;
sub();

答案有兩種狀況,分爲嚴格模式和非嚴格模式。

  • 嚴格模式下,報錯。TypeError: Cannot read property 'num' of undefined
  • 非嚴格模式下,輸出:一、三、三、四、4

解答過程:

var num = 1;
var myObject = {
    num: 2,
    add: function() {
        this.num = 3; // 隱式綁定 修改 myObject.num = 3
        (function() {
            console.log(this.num); // 默認綁定 輸出 1
            this.num = 4; // 默認綁定 修改 window.num = 4
        })();
        console.log(this.num); // 隱式綁定 輸出 3
    },
    sub: function() {
        console.log(this.num) // 由於丟失了隱式綁定的myObject,因此使用默認綁定 輸出 4
    }
}
myObject.add(); // 1 3
console.log(myObject.num); // 3
console.log(num); // 4
var sub = myObject.sub;//  丟失了隱式綁定的myObject
sub(); // 4

內容來自評論區:【進階3-1期】JavaScript深刻之史上最全--5種this綁定全面解析

本期思考題

分別給出console.log輸出的內容。

var obj = {
    say: function () {
        function _say() {
            console.log(this);
        }
        console.log(obj);
        return _say.bind(obj);
    }()
}
obj.say()

參考

從這兩套題,從新認識JS的this、做用域、閉包、對象

進階系列目錄

  • 【進階1期】 調用堆棧
  • 【進階2期】 做用域閉包
  • 【進階3期】 this全面解析
  • 【進階4期】 深淺拷貝原理
  • 【進階5期】 原型Prototype
  • 【進階6期】 高階函數
  • 【進階7期】 事件機制
  • 【進階8期】 Event Loop原理
  • 【進階9期】 Promise原理
  • 【進階10期】Async/Await原理
  • 【進階11期】防抖/節流原理
  • 【進階12期】模塊化詳解
  • 【進階13期】ES6重難點
  • 【進階14期】計算機網絡概述
  • 【進階15期】瀏覽器渲染原理
  • 【進階16期】webpack配置
  • 【進階17期】webpack原理
  • 【進階18期】前端監控
  • 【進階19期】跨域和安全
  • 【進階20期】性能優化
  • 【進階21期】VirtualDom原理
  • 【進階22期】Diff算法
  • 【進階23期】MVVM雙向綁定
  • 【進階24期】Vuex原理
  • 【進階25期】Redux原理
  • 【進階26期】路由原理
  • 【進階27期】VueRouter源碼解析
  • 【進階28期】ReactRouter源碼解析

交流

進階系列文章彙總:https://github.com/yygmind/blog,內有優質前端資料,歡迎領取,以爲不錯點個star。

我是木易楊,網易高級前端工程師,跟着我每週重點攻克一個前端面試重難點。接下來讓我帶你走進高級前端的世界,在進階的路上,共勉!

相關文章
相關標籤/搜索