關於js 中this指向問題

如下內容爲本人閱讀《你不知道的Javascript》中this部分的讀書小結,有興趣的話能夠直接看原著。app

前言

  • 初步瞭解this
    1.this不指向函數自身
    2.this不指向函數的詞法做用域(箭頭函數除外)
    3.this實際上實在函數調用時發生的綁定,它指向什麼徹底取決於函數在哪裏調用,也就是函數的調用位置。

綁定規則

默認綁定

  • 默認綁定中,this 指向全局對象
  • 若是判斷是不是默認綁定?
    觀察函數的調用位置,不帶任何修飾的函數引用進行調用,適用默認綁定。注意嚴格模式下,不能將全局對象用於默認綁定,this 會綁定到undefined。函數

    fucntion foo() {
     console.log(this.a) 
    }
    var a = 2;
    foo()  // 2

    隱式綁定

  • 隱式綁定規則會把函數調用中的this綁定到這個上下文對象。
  • 觀察調用位置是否有上下文對象,或者被某個對象擁有或者包含,以下:this

    fucntion foo() {
       console.log(this.a) 
    }
    var obj = {
        a: 2,
        foo: foo
    }
    obj.foo()  // 2
  • 對象屬性引用鏈上只有最後一層在調用位置中起做用。es5

    function foo() {
        console.log(this.a)
    }
    var obj1 = {
        a: 2,
        foo: foo
    }
    var obj2 = {
        a: 42,
        obj1: obj1
    }
    obj2.obj1.foo()   //2
  • 函數引用致使隱式綁定丟失prototype

    1.函數引用code

var a = 'global'
  function foo() {
      console.log(this.a)
  }
  var obj1 = {
      a: 2,
      foo: foo
  }
  var bar = obj1.foo
  bar()   // 'global'

​ 2.參數傳遞或者回調函數(本質是隱式賦值)對象

var a = 'global'
  function foo() {
      console.log(this.a)
  }
  var obj1 = {
      a: 2,
      foo: foo
  }
  var bar = obj1.foo
  function bar(fn) {
      fn()
  }
  bar(obj1.foo)   // 'global'
  
  setTimeout(obj1.foo, 500) //'global'

顯式綁定

  • call(obj, ...args)
  • apply(obj, args)
  • bind(obj, ...args) es5硬綁定

New 綁定

  • 使用new來調用函數時,咱們會構造一個新對象並把它綁定到函數調用中的this上。
function foo(val) {
        this.a = val
    }
    let bar = new foo(2)
    console.log(bar.a )   // 2 
    // 此時函數中的this => bar。
    // 簡單瞭解一下使用new來調用函數,會發生什麼
    // 1.建立(或者說構造)一個全新的對象。
    // 2.這個新對象會被執行[[Prototype]]鏈接。
    // 3.這個新對象會綁定到函數調用的this。
    // 4.若是函數沒有返回其餘對象,那麼new表達式中的函數調用會自動返回這個新對象。
    // 也就是在第3步的時候纔會進行this綁定,更體現了this綁定是發生在函數調用的時候。

優先級比較

隱式綁定和默認綁定(顯而易見)

顯而易見,隱式綁定優先級高於默認綁定

顯示綁定和隱式綁定

function foo() {
        console.log(this.name)
    }
    let obj1 = {
        name: 'obj1',
        foo
    }
    let obj2 = {
        name: 'obj2',
    }
        obj1.foo.call(obj2)  // 'obj2'

顯式綁定優先級高於隱式綁定繼承

new綁定和隱式綁定

function foo(name) {
        this.name = name
    }
    let obj1 = {
        name: 'obj1',
        foo
    }
    obj1.foo('new name') 
    console.log(obj1.name)          // new name
    var bar = new obj1.foo('bar')   
    console.log(bar.name)          // bar
    console.log(obj1.name)         // new name

new綁定優先級高於隱式綁定ip

new綁定和顯示綁定

  • ES5中內置的Function.prototype.bind(..)作用域

    function foo(name) {
          this.name = name
      }
      let obj1 = {
          name: 'obj1',
          foo
      }
      var newFoo = foo.bind(obj1)
      newFoo('new name')
      console.log(obj1.name)         // new name
      var bar = new newFoo('bar')   
      console.log(bar.name)          // bar
      console.log(obj1.name)         // new name

如和判斷this(通常狀況下(非箭頭函數),判斷流程)

1.函數是否在new中調用(new綁定)?若是是的話this綁定的是新建立的對象。
2.函數是否經過call、apply(顯式綁定)或者硬綁定調用?若是是的話,this綁定的是指定的對象。
3.函數是否在某個上下文對象中調用(隱式綁定)?若是是的話,this綁定的是那個上下文對象。
4.若是都不是的話,使用默認綁定。若是在嚴格模式下,就綁定到undefined,不然綁定到全局對象。
(例外:若是你把null或者undefined做爲this的綁定對象傳入call、apply或者bind,這些值在調用時會被忽略,實際應用的是默認綁定規則)

箭頭函數

1.箭頭函數不使用this的四種標準規則,而是根據外層(函數或者全局)做用域來決定this。
2.箭頭函數的綁定沒法被修改
3.箭頭函數用更常見的詞法做用域取代了傳統的this機制。
4.在沒有箭頭函數以前,咱們一般使用 that 或者 self,去代替函數內部的this

this.a = 'global'
    var obj1 = {
        a: 1
    }
    function foo() {
        setTimeout_(() => {
            console.log(this.a)
        })
    }
    
    function bar() {
        setTimeout_(function() {
            console.log(this.a)
        })
    }
    function setTimeout_(fn) {
        fn()
    }
    foo.call(obj1) // 1       箭頭函數,須要觀察箭頭函數被建立時的詞法做用域,this取決於外層做用域
    bar.call(obj1) // global  普通函數使用四條規則,主要觀察函數被調用時的狀況

總結

  • 若是要判斷一個運行中函數的this綁定,就須要找到這個函數的直接調用位置。找到以後就能夠順序應用下面這四條規則來判斷this的綁定對象。
    1.由new調用?綁定到新建立的對象。
    2.由call或者apply(或者bind)調用?綁定到指定的對象。
    3.由上下文對象調用?綁定到那個上下文對象。
    4.默認:在嚴格模式下綁定到undefined,不然綁定到全局對象。必定要注意,有些調用可能在無心中使用默認綁定規則。
  • ES6中的箭頭函數並不會使用四條標準的綁定規則,而是根據當前的詞法做用域來決定this,具體來講,箭頭函數會繼承外層函數調用的this綁定(不管this綁定到什麼)。這其實和ES6以前代碼中的self = this機制同樣。
相關文章
相關標籤/搜索