一篇文章完全搞懂this

函數(shù)中this的指向

先測一下你是否真的明白了this的指向

foo.count=0
function foo(num){
    this.count++
}

for(let i=0;i<10;i++){
    if(i>5){
        foo(i)
    }
}

console.log(foo.count) //0

你可能一臉茫然道:這不是應(yīng)該輸出4嗎??咋輸出0捏。(尼克楊臉)

foo是執(zhí)行了4次沒錯。但this在任何情況下都不指向函數(shù)的詞法作用域??!foo中的this指向全局,這是因為this是在運行時綁定的,并不是在編寫時綁定的。它的上下文取決于函數(shù)調(diào)用時的各種條件。this的綁定和函數(shù)聲明的位置沒有關(guān)系,它只取決于函數(shù)的調(diào)用方式。當(dāng)一個函數(shù)被調(diào)用時,會創(chuàng)建一個執(zhí)行上下文,它包含函數(shù)在哪里被調(diào)用、函數(shù)的調(diào)用方式、傳入的參數(shù)等信息,this就是這個記錄的一個屬性,在函數(shù)執(zhí)行的過程中會用到。

那我們回到上面的程序,要想讓控制臺正確打印foo被調(diào)用的次數(shù),只要把this.count++改為foo.count++就好啦~

總的來說呢,就是函數(shù)中的this指向什么,取決于這個函數(shù)在哪里被調(diào)用。

this的綁定規(guī)則

1.默認(rèn)綁定

這是最常用的函數(shù)調(diào)用類型:獨立函數(shù)調(diào)用,可以把這條規(guī)則看作是無法應(yīng)用其他規(guī)則時的默認(rèn)規(guī)則。思考一下下面的代碼:

function foo(){
    console.log(this.a)
}
var a=1
foo() // 1

因為在全局環(huán)境中調(diào)用了foo,this.a被解析為全局對象a。這時沒有使用任何修飾的函數(shù)進行調(diào)用,所以實現(xiàn)了this的默認(rèn)綁定,指向全局對象。

2.隱式綁定

調(diào)用位置是否有上下文,換句話說是否被某個對象擁有或包含。思考下面的代碼:

var a=1
function foo(){
    console.log(this.a)
}
const obj={a:2,foo}
obj.foo() // 2

當(dāng)函數(shù)引用由上下文對象時,隱式綁定規(guī)則會把函數(shù)調(diào)用中的this綁定到這個上下文對象。對象屬性的引用鏈只有最后一層在調(diào)用位置中起作用。如:

function foo(){
    console.log(this.a)
}
const obj1={a:1,foo}
const obj2={a:2,obj1}

obj2.obj1.foo() //1

隱式丟失

還是看上面那段代碼,如果后面加一句:

const bar=obj1.foo
bar() //undefined

這就是隱式丟失,const bar=obj1.foo這句其實是一個引用,它引用的是foo函數(shù)本身。此時的bar其實是一個不帶任何修飾的函數(shù)調(diào)用,因此進行了默認(rèn)綁定,this指向全局。所以為undefined。

更常見的一種狀況發(fā)生在傳入回調(diào)函數(shù)時:

function doFoo(fn){
    fn()
}
const obj={a:1,foo}
doFoo(obj.foo) //undefined

參數(shù)傳遞也是一種隱式賦值,我們傳入函數(shù)時fn也是引用的foo函數(shù)本身,原理同上個例子。

3.顯式綁定

顯示綁定一般是通過call和apply來實現(xiàn)。我們還是用foo函數(shù)來說明。

foo.call(obj1)
foo() //1

1.硬綁定

const bar=function (){
    foo.call(obj1)
}
bar() //1

這里強制把foo的this綁定到obj上,無論之后如何調(diào)用bar,它總會手動在obj上調(diào)用foo。這種綁定是一種顯式的強制綁定,稱之為硬綁定。

硬綁定是一種非常常用的模式,es5提供了內(nèi)置方法Function.prototype.bind,它的用法如下:

function foo(something){
    return this.a+something
}
const obj={a:1}
const bar=foo.bind(obj)

const b=bar(3)
console.log(b) // 4

2.API調(diào)用的上下文

第三方庫的許多函數(shù),以及js語言和宿主環(huán)境中許多新的內(nèi)置函數(shù),都提供了可選的參數(shù),通常被稱為上下文,其作用和bind一樣。

舉個例子:

function foo(el){
    console.log(el,this.id)
}
const obj={id:'awesome'}
// 調(diào)用foo時把this綁定到obj
[1,2,3].forEach(foo,obj)
// 1 awesome 2 awesome 3 awesome

4.new綁定

內(nèi)置對象函數(shù)在內(nèi)的所有函數(shù)都可以用new來調(diào)用,這種函數(shù)調(diào)用被稱為構(gòu)造函數(shù)調(diào)用。使用new來調(diào)用函數(shù),會自動執(zhí)行下面的操作:

  1. 創(chuàng)建一個全新的對象
  2. 這個新對象會被執(zhí)行prototype連接
  3. 這個新對象會綁定到函數(shù)調(diào)用的this
  4. 如果函數(shù)沒有返回其他對象,那么new表達(dá)式中的函數(shù)調(diào)用會自動返回這個新對象。

思考下面的代碼:

function foo(a){
    this.a=a
}
const bar=new foo(2)
console.log(bar.a) // 2

使用new來調(diào)用foo時,我們會構(gòu)造一個新對象并把它綁定到foo調(diào)用中的this上。

優(yōu)先級

new綁定 > 顯式綁定 > 隱式綁定 > 默認(rèn)綁定

在new中使用硬綁定,主要是預(yù)先設(shè)置函數(shù)的一些參數(shù),這樣在new初始化時,可以傳入其余的參數(shù)。bind的功能之一就是把除了第一個參數(shù)(用于綁定this)之外的其他參數(shù)都傳給下層的參數(shù),這種技術(shù)稱為柯里化。舉個例子:

function foo(p1,p2){
    this.val=p1+p2
}
const bar=foo.bind(null,"hello")
const baz=new bar("world")

console.log(baz.val) // helloworld

綁定例外

1. 當(dāng)我們把null、undefined作為this傳入call、apply或bind,這些值在調(diào)用時會被忽略,實際應(yīng)用的是默認(rèn)綁定規(guī)則。

2. 間接引用的情況下會應(yīng)用默認(rèn)綁定規(guī)則。

舉個例子:

   function foo(){
       console.log(this.a)
   }
   var a=0
   var obj1={a:1,foo}
   var obj2={a:2}
   
   obj1.foo() //1
   (obj2.foo=obj1.foo)() // 0 

表達(dá)式obj2.foo=obj1.foo的返回值為目標(biāo)函數(shù)的引用,這里會應(yīng)用默認(rèn)綁定。

3. 箭頭函數(shù)

前面介紹的四種規(guī)則適用于除了箭頭函數(shù)之外,所有的普通函數(shù)。我們先看看箭頭函數(shù)的詞法作用域:

function foo(){
    // this繼承自foo
    return a=>{console.log(this.a)}
}

var obj1={a:1}
var obj2={a:2}

const bar=foo.call(obj1)
bar.call(obj2) // 是1,不是2

因為箭頭函數(shù)會補貨調(diào)用時foo()this。由于foo()的this綁定到obj1,bar的this也會綁定到obj1,箭頭函數(shù)的綁定無法被修改。

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

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

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