深入了解javascript中的this

this 作為js中的常用引用,區(qū)別與詞法作用域,有自己一套的規(guī)則

this的綁定規(guī)則

函數(shù)在執(zhí)行過程中的調(diào)用位置決定了this的綁定對(duì)象,因此需要確定調(diào)用的位置,才能準(zhǔn)確判斷this綁定的對(duì)象是誰。

  • 默認(rèn)綁定

    • 當(dāng)函數(shù)獨(dú)立調(diào)用的時(shí)候默認(rèn)綁定的this 指向?yàn)槿謱?duì)象,當(dāng)在函數(shù)內(nèi)部使用嚴(yán)格模式use strict時(shí),this為undefine。

    • function foo() {
        console.log(this.a)
      }
      function bar() {
        'use strict';
        console.log(this.a)
      }
      const a = 1
      foo() // -> 1 注意:在node環(huán)境中為 undefined
      bar() // -> TypeError: this is undefined
      
    • 在node環(huán)境中 this 的 在默認(rèn)綁定情況下 為Object [global] , 無法向上查詢到在全局下賦值的屬性。

      // node 環(huán)境中
      function foo() {
        console.log(this.a)
      }
      
      const a = 1
      foo() // -> undefined
      
      this.a = 1
      console.log(this.a) // -> 1
      
  • 隱式綁定

    • 通過將函數(shù)引用進(jìn)一個(gè)對(duì)象的屬性,通過對(duì)象調(diào)用函數(shù)的形式實(shí)現(xiàn)

      function foo() {
        console.log(this.a)
      }
      
      const obj = {
          a: 1,
          foo: foo
      }
      
      const a = 'global a'
      obj.foo() // -> 1
      
      // 可能會(huì)存在this 丟失,轉(zhuǎn)而進(jìn)行默認(rèn)綁定(同默認(rèn)綁定)
      // 1. 當(dāng)進(jìn)行再次引用的時(shí)候
      var bar = obj.foo // 定義bar 指向foo函數(shù)
      bar() // -> global a
      // 2. 當(dāng)將函數(shù)作為參數(shù)傳遞的時(shí)候
      setTimeout(obj.foo, 1000) // -> global a
      
  • 顯示綁定

    • 通過apply, call , bind進(jìn)行顯示的綁定this 對(duì)象

      function foo(){
        console.log(this.a)
      }
      
      const obj = {
        a : 1
      }
      // foo.call(obj, 可變參數(shù)) 
      foo.call(obj) // -> 1
      // foo.apply(obj, 數(shù)組) 
      foo.apply(obj) // => 1
      
      const bar = foo.bind(obj)
      bar() // 1
      
  • new綁定

    • JavaScript 中構(gòu)造函數(shù)知識(shí)一些使用new 操作符時(shí)被調(diào)用的函數(shù),并不屬于某個(gè)類,也不會(huì)實(shí)例化一個(gè)類。new 調(diào)用某個(gè)函數(shù)的時(shí)候做了以下事情:

        1. 創(chuàng)建一個(gè)全新的對(duì)象
        2. 這個(gè)新的對(duì)象會(huì)被執(zhí)行[[Proptype]]連接
        3. 這個(gè)新的對(duì)象會(huì)綁定到函數(shù)調(diào)用的this
        4. 如果該函數(shù)沒有返回對(duì)象,那么new 表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新的對(duì)象
      function foo(a) {
          this.a = a 
      }
      
      var bar = foo(1)
      console.log(bar.a) // -> 1
      

this綁定中的優(yōu)先級(jí)

  • 函數(shù)中如果存在new綁定,如果是的話,this綁定的就是新創(chuàng)建的對(duì)象。
  • 函數(shù)中如果存在call, apply,bind 的綁定,this 綁定的是指定的對(duì)象。
  • 函數(shù)中如果存在某個(gè)對(duì)象中的引用,并被該對(duì)象調(diào)用(隱式綁定),this綁定的是那個(gè)對(duì)象。
  • 如果以上都不是的話,則使用默認(rèn)綁定,如果在嚴(yán)格模式下,則綁定到 undefined
  • 需要注意的是,在node環(huán)境中,默認(rèn)綁定總是不盡人意的。

特殊場景下的this

  • 當(dāng)使用顯示綁定的時(shí)候,如果傳入的綁定對(duì)象為null 或者 undefined , 那么函數(shù)中的this 會(huì)作為默認(rèn)綁定指向全局

    foo() {
        console.log(this.a)
    }
    const obj = {
        a:1
    }
    const a = 'global a'
    foo.call(null) // -> global a
    
    
  • 當(dāng)使用 new 包裹 顯示綁定的時(shí)候,顯示綁定的對(duì)象會(huì)失效, 這時(shí)候的 this 會(huì)綁定到新創(chuàng)建的對(duì)象上,一般用這種寫法來:

    • 創(chuàng)建柯里化函數(shù)。
    • 展開一個(gè)數(shù)組,當(dāng)參數(shù)傳入函數(shù)。
    function foo(p1, p2) {
       this.a = p1 + p2
    }
    
    var obj = {
        a : 'a'
        
    }
    
    //  函數(shù)的柯里化
    //const bar = foo.bind(obj, 'p1')
    const bar = foo.bind(null, 'p1')
    const baz = new bar('p2') // new 后 上一行代碼中的obj 會(huì)被新的baz上下文所替代,所以可以直接寫null
    console.log(baz.a) // -> 'p1p2'
    
    // 展開數(shù)組
    foo.apply(null, ['p1', 'p2'])
    
    

    函數(shù)的柯里化就是通過bind 預(yù)先設(shè)置一些參數(shù),然后在通過函數(shù)調(diào)用傳遞另外一些參數(shù),這里模擬一下函數(shù)的原理:

    function curry(fn) {
        const outArgs = Array.prototype.slice.call(arguments, 1)
        
        return function() {
            const innerArgs = Array.prototype.slice.call(arguments)
            const finalArgs = outArgs.concat(innerArgs)
            return fn.apply(null, finalArgs)
        }
    }
    
  • 當(dāng)使用 fn.apply(null, args) 的時(shí)候,因?yàn)?code>null 的存在會(huì)是的函數(shù)中的 this指向全局變量,可能會(huì)出現(xiàn)不可估計(jì)的后果(比如對(duì)全局進(jìn)行操作修改之類的)。這個(gè)時(shí)候建議 使用空對(duì)象替換null,因此上面的代碼修改如下:

function foo(p1, p2) {
   console.log('p1:' + p1 + '; p2:' + p2)
}

// 創(chuàng)建空對(duì)象 同{} 相比, 不包含Object.prototype
const ? = Object.create(null)

//  函數(shù)的柯里化
const bar = foo.bind(?, 'p1')
bar('p2')

// 展開數(shù)組
foo.apply(?, ['p1', 'p2'])


  • 當(dāng)然,也可以重寫Prototype中的bind 方法進(jìn)行定制this, 當(dāng)使用的是全局或者undefined的時(shí)候,可以讓this 指向傳遞進(jìn)來的對(duì)象

  • 當(dāng)使用箭頭函數(shù)的時(shí)候,不會(huì)遵循上面的使用規(guī)則,而是根據(jù) 詞法作用域來決定this, 準(zhǔn)確的說法是 箭頭函數(shù)會(huì)繼承外層函數(shù)調(diào)用的this綁定

    function foo() {
        setTimeout(()=> {
            console.log(this.a) // 這里的this是foo作用域下的this
        }, 1000)
        
        // => 等同于
        const self = this
        setTimeout(function() {
            console.log(this.a)
        }, 1000)
    }
    
    var obj = {
        a : 1
    }
    
    foo.call(obj) // 1
    
    
    

以上是我對(duì)JavaScript中的this用法的理解與總結(jié),希望能幫到大家,如果能夠幫到您,希望不吝點(diǎn)個(gè)贊哦;如果哪里寫的不對(duì)或者缺失,還望告知~~

參考文獻(xiàn)

你不知道的JavaScript

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容