this是什麼以及如何判斷它

weixin_34127717發表於2019-02-19

總結下對this的學習與理解

轉眼前端的學習已有一年,日常寫程式碼中經常碰到this這個東西。特別是在用vue的時候,this還是有點多的,哈哈。
在翻閱了一部分書籍和一堆大佬的部落格後,決定總結一下這些東西,下面談談我對this的一些理解,如果有錯誤,歡迎大家批評指正。如果可以給你帶來一些幫助,那是再好不過的了。

1.什麼是this

this是在執行時基於函式的執行環境繫結的,它的上下文取決於函式呼叫時的各種條件。
當一個函式被呼叫時,會建立一個活動記錄(有時候也稱執行上下文)。這個記錄會包含函式在哪裡被呼叫,函式的呼叫方法、出今年入的引數等資訊。this就是記錄其中一個屬性,會在函式執行的過程中用到 --你不知道的JavaScript(上)

this實際上是在函式被呼叫時發生的繫結


2 如何判斷this繫結的物件是什麼

var name = 'window'
const obj1 = {
  
  name:'obj1',
  fn(){
      console.log(this.name)      
  }
}
obj1.fn()//'obj1'
var name = 'window'
const obj1 = {
  
  name:'obj1',
  fn(){
//    "use strict"
   
      console.log(this.name)
       
  }
  
}
const obj2 = {
  name:'obj2',
  fn2:obj1.fn
}
obj2.fn2()//'obj2'
obj1.fn()//'obj1'

#this的繫結與函式宣告的位置沒有宣告的位置沒有任何關係,只取決於函式的呼叫方式
#一般來說,誰最終呼叫了函式,那麼它就是this的繫結物件。
那在全域性下呼叫函式,this的繫結物件也就是全域性物件

var name = 'window'
function fn(){
 
  console.log(this.name)
}
fn()//'window'

下面這種情況為什麼不是輸出window呢?

var name = 'window'
function fn(){
 this.name = 'fn'
  console.log(this.name)
}
fn()// 'fn'

也是因為在全域性呼叫這個函式的時候,this.name === window.name就把之前的值給覆蓋了,所以輸出就是 'fn'。

3. 那麼如何判斷最終是誰呼叫了函式

函式呼叫的形式

var name = 'window'
const obj1 = {
  
  name:'obj1',
  fn(){
//    "use strict"
   
      console.log(this.name)
       
  }
  
}
const obj2 = {
  name:'obj2',
  fn2:obj1.fn
}
obj2.fn2()//'obj2'
obj1.fn()//'obj1'

還是按照上面的程式碼為例,這裡

obj2.fn2()//完全等價於下面的
obj2.fn2.call(obj2)

obj1.fn() 等於 obj1.fn.call(obj1)

同樣的

var name = 'window'
function fn(){
 this.name = 'fn'
  console.log(this.name)
}
fn() 等於 fn.call()

這裡面call不傳入引數,既為undefined。在非嚴格模式下傳入undefined和null,都預設是window。

 
var name = 'window'
function fn(){
  'use strict' //為函式開啟嚴格模式
 this.name = 'fn'
  console.log(this.name)
}
fn() //則會報錯,這也同樣說明了函式在js內部呼叫的形式

改改前面的程式碼

var name = 'window'
const obj1 = {
  
  name:'obj1',
  fn(){
        this.name = 'fn'
      function inFn(){
        console.log(this.name)
      }
       inFn()
  }
  
}

obj1.fn()//'window '

上面的inFn()也等同於inFn.call(),在全域性呼叫它,自然也就是輸出window了。

關於箭頭函式和定時器

首先箭頭函式是沒有this的 ,它的作用域是和父級的上下文繫結在一起的

var name = 'window'
const obj1 = {
  
  name:'obj1',
  fn(){
        this.name = 'fn'
     inFn= ()=>{
        console.log(this.name)
      }
       inFn()
  }
  
}

obj1.fn()//'fn'

它的this就是上一層第一個包裹它的普通函式的this
所以用call/bind/apply呼叫箭頭函式的時候,是沒有效果的。

var name = 'window'
const obj1 = {
  
  name:'obj1',
  fn(){
        this.name = 'fn'
     inFn= ()=>{
        console.log(this.name)
      }
       inFn()
  }
  
}

obj1.fn.call(window)//'fn'

這也是為什麼在es6後,諸如_this=this這類寫越來越少的原因

定時器對this的影響

setTimeout()呼叫的程式碼執行在與所在函式完全分離的執行環境上。這會導致,這些程式碼中包含的 this 關鍵字在非嚴格模式會指向 window (或全域性)物件,嚴格模式下為 undefined,這和所期望的this的值是不一樣的。--MDN

備註:在嚴格模式下,setTimeout( )的回撥函式裡面的this仍然預設指向window物件, 並不是undefined


this.name = 'window'
function Foo(){
this.name = 'Foo'
  this.fn = function(){
    console.log(this.name)
  }
  this.timer = function(){
    setTimeout(this.fn)
  }
}
var obj = new Foo()

obj.timer()//'window'

可以使用call/apply/bind呼叫改變this繫結物件。


this.name = 'window'
function Foo(){
this.name = 'Foo'
  this.fn = function(){
    console.log(this.name)
  }
  this.timer = function(){
    setTimeout(this.fn.call(this))
  }
}
var obj = new Foo()

obj.timer()//'Foo'

還有箭頭函式


this.name = 'window'

const obj = {
  name:'obj',
  fn(){
    console.log(1)
    setTimeout(()=>{
      console.log(this.name)
    },1000)
  }
}

obj.fn()//'obj'

也就是說從優先順序上說,有箭頭函式和定時器就不判斷是否由call/apply/bind呼叫

new對this的影響

new的優先順序很高,在沒有定時器影響的情況下是最高的

this.name = 'window'
function Foo(){
this.name = 'Foo'
  this.fn = function(){
    console.log(this.name)
  }
  this.timer = function(){
    setTimeout(this.fn)
  }
}
var obj = new Foo()

obj.timer() //'window'定時器

只要是由new呼叫的,就繫結到新建立的物件,其他情況似乎都無法再改變this繫結的物件。

1.箭頭函式

function Foo(){
  this.name='Foo'
  this.fn = ()=>{
    console.log(this.name)
  }
  
}

var foo = new Foo()
this.name = 'window'  //設定一下window.name的值      
foo.fn()    //'Foo' 輸出的是例項變數
console.log(this.name)//'window'  並沒有改變window.name的值 

2.call/bind/apply

this.name = 'window'
function Foo(){
  this.name='Foo'
  this.fn = ()=>{
    console.log(this.name)
  }
  
}

var foo = new Foo()

foo.fn.call(window)//'Foo'  
const tes = foo.fn.bind(window)
tes()  //'Foo'

##也就是說判斷一個執行中的this繫結,就要找到這個函式的直接呼叫位置。
##按照上面介紹的優先順序順序進行判斷便可以找到this的繫結物件。

相關文章