JavaScript中this的綁定你知道幾種?
執(zhí)行上下文
我們知道執(zhí)行上下文分為兩種:全局上下文和函數(shù)上下文(我的這篇文章對(duì)于執(zhí)行上下文有講解還對(duì)執(zhí)行上下文和作用域迷糊嗎?)。全局上下文只有一個(gè),函數(shù)執(zhí)行上下文是在函數(shù)調(diào)用的時(shí)候創(chuàng)建的。
每個(gè)執(zhí)行上下文都有三個(gè)屬性:
- 變量對(duì)象
- 作用域鏈
- this
this到底是什么呢
this是在運(yùn)行時(shí)綁定的,并不是在編寫(xiě)時(shí)綁定的,它的上下文取決于函數(shù)調(diào)用時(shí)的各種條件。this的綁定和函數(shù)聲明的位置沒(méi)有任何的關(guān)系,只取決于函數(shù)的調(diào)用方式。
調(diào)用位置
要理解this的綁定過(guò)程,首先要理解調(diào)用位置。調(diào)用位置就是函數(shù)的調(diào)用的位置(不是聲明的位置)。所以我們要先來(lái)分析調(diào)用棧(也就是執(zhí)行上下文棧)。我們先來(lái)看一段代碼。
function baz() { console.log("baz") bar() } function bar() { console.log("bar") foo() } function foo() { console.log("foo") } baz()
當(dāng)代碼執(zhí)行到foo(),進(jìn)入foo的函數(shù)體,此時(shí)當(dāng)前的調(diào)用棧為:
ECStack = [ fooContext, // foo barContext, // bar bazContext, // baz globalContext, // 全局 ]
通過(guò)調(diào)用棧我們就可以很清晰的找到函數(shù)的調(diào)用位置。baz在全局調(diào)用,bar在baz里調(diào)用,foo在bar里調(diào)用。
那函數(shù)在執(zhí)行的時(shí)候是如何決定this的綁定對(duì)象的呢?
綁定規(guī)則
通過(guò)綁定規(guī)則決定this的綁定對(duì)象。
默認(rèn)綁定
最常用的調(diào)用類(lèi)型:獨(dú)立函數(shù)調(diào)用。
function foo(){ console.log(this.a) // 2 } var a = 2; foo()
函數(shù)調(diào)用的時(shí)候,使用了this的默認(rèn)綁定,因此this指向全局對(duì)象。
那么我們?cè)趺粗肋@里應(yīng)用了默認(rèn)綁定呢?可以通過(guò)分析調(diào)用位置來(lái)看看 foo() 是如何調(diào)用的。在代碼中,foo()是直接使用不帶任何修飾的函數(shù)引用進(jìn)行調(diào)用的,因此只能使用默認(rèn)綁定,無(wú)法應(yīng)用其他規(guī)則。
所以,在全局環(huán)境中調(diào)用一個(gè)函數(shù),函數(shù)內(nèi)部的this指向的是全局變量window。
隱式綁定
function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; obj.foo(); // 2
這段代碼我們看到foo的聲明位置是在全局的,但是它被當(dāng)做引用屬性添加到了obj中。調(diào)用位置使用obj上下文引用函數(shù)。當(dāng)foo被調(diào)用時(shí)候,它是被obj對(duì)象所包含的,落腳點(diǎn)指向obj對(duì)象。當(dāng)函數(shù)引用有上下文對(duì)象時(shí),隱式綁定規(guī)則會(huì)把函數(shù)調(diào)用中的this綁定到這個(gè)上下文對(duì)象。
通過(guò)一個(gè)對(duì)象調(diào)用其內(nèi)部的一個(gè)方法,該方法的執(zhí)行上下文中的this指向?qū)ο蟊旧?/strong>
我們看個(gè)特殊的例子
function foo() { console.log(this.a) } var obj = { a: 2, foo } var bar = obj.foo var a = "mick" bar() // mick
bar是obj.foo的一個(gè)引用,但是實(shí)際上,它引用的是foo函數(shù)本身,因此此時(shí)bar()其實(shí)是一個(gè)不帶任何修飾的函數(shù)調(diào)用,因此應(yīng)用了默認(rèn)綁定。
我們?cè)倏戳硪环N情況
function foo() { console.log(this.a) } function doFoo(fn) { fn() } var obj = { a: 2, foo } var a = "mick" doFoo(obj.foo) // mick
嵌套函數(shù)中的this 不會(huì)從外層函數(shù)中繼承。this永遠(yuǎn)指向最后調(diào)用它的那個(gè)對(duì)象
顯示綁定
可以使用call、apply或bind方法。如果對(duì)這三個(gè)方法的實(shí)現(xiàn)原理感興趣可以看看這篇手寫(xiě)call、apply、bind
function foo() { console.log(this.a) } var obj = { a: 2 } foo.call(obj) // 2
通過(guò)call方法,可以在調(diào)用foo時(shí)候,強(qiáng)制把它的this綁定到obj上。
new綁定
這里我們先說(shuō)一下new來(lái)調(diào)用函數(shù)會(huì)發(fā)生哪些事情
- 創(chuàng)建一個(gè)全新的對(duì)象
- 這個(gè)新對(duì)象會(huì)被執(zhí)行[[原型]]鏈接
- 這個(gè)新對(duì)象會(huì)綁定到函數(shù)調(diào)用的this
- 如果函數(shù)沒(méi)有返回其他對(duì)象,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象。
function foo(a){ this.a = a } var bar = new foo(2) console.log(bar.a)
使用new來(lái)調(diào)用foo時(shí),我們會(huì)構(gòu)造一個(gè)新對(duì)象并把它綁定到foo調(diào)用中的this上。
特例
function foo() { console.log(this.a) } var a = 2 var o = { a: 3, foo: foo } var p = { a: 4 } o.foo() // 3 ;(p.foo = o.foo)() // 2
賦值表達(dá)式p.foo = o.foo
的返回值是目標(biāo)函數(shù)的引用,因此調(diào)用位置是foo()而不是p.foo()或者o.foo()。所以這里是默認(rèn)綁定。
面試題
下面我們看個(gè)面試題吧
var name = 'window' var person1 = { name: 'person1', foo1: function () { console.log(this.name) }, foo2: () => console.log(this.name), foo3: function () { return function () { console.log(this.name) } }, foo4: function () { return () => { console.log(this.name) } } } var person2 = { name: 'person2' } person1.foo1() person1.foo1.call(person2) person1.foo2() person1.foo2.call(person2) person1.foo3()() person1.foo3.call(person2)() person1.foo3().call(person2) person1.foo4()() person1.foo4.call(person2)() person1.foo4().call(person2)
我們一個(gè)個(gè)來(lái)解析一下。
person.foo1()
這個(gè)屬于隱式綁定,foo1的this綁定到了person,所以打印person1person1.foo1.call(person2)
顯示綁定,foo1的this通過(guò)call改變了this,指向了person,所以打印person2person.foo2()
因?yàn)閒oo2是箭頭函數(shù),所以它的this指向是它上一層this的指向也就是window,windowperson1.foo2.call(person2)
和上面一條同樣的道理,也是windowperson1.foo3()()
內(nèi)部返回了一個(gè)函數(shù),其實(shí)是一個(gè)函數(shù)的引用,此時(shí)this應(yīng)該指向window,所以打印windowperson1.foo3.call(person2)()
通過(guò)call只是改變了foo3的this指向,和返回的函數(shù)沒(méi)有什么關(guān)系,所以this還是指向window,打印windowperson1.foo3().call(person2)
通過(guò)call改變了foo3內(nèi)部返回函數(shù)的this指向,所以打印person2person1.foo4()()
返回的是一個(gè)箭頭函數(shù),箭頭函數(shù)的this是它上一層函數(shù)內(nèi)部this的指向,所以也就是foo4this的指向,由于foo4被person1包含并調(diào)用,所以this指向person1,打印person1person1.foo4.call(person2)()
,此時(shí)foo4內(nèi)部的this通過(guò)call改變成了person2,所以打印personperson1.foo4().call(person2)
這個(gè)和person1.foo4()
是一樣的道理,打印person1
簡(jiǎn)單的談了談this的綁定,歡迎留言你的問(wèn)題,大家一起學(xué)習(xí)一起進(jìn)步?。。?/p>
到此這篇關(guān)于JavaScript中this的綁定你知道幾種?的文章就介紹到這了,更多相關(guān)JavaScript this綁定內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
layui自定義插件citySelect實(shí)現(xiàn)省市區(qū)三級(jí)聯(lián)動(dòng)選擇
這篇文章主要為大家詳細(xì)介紹了layui自定義插件citySelect實(shí)現(xiàn)省市區(qū)三級(jí)聯(lián)動(dòng)選擇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07Web?Components使用生命周期回調(diào)函數(shù)實(shí)例詳解
這篇文章主要為大家介紹了Web?Components使用生命周期回調(diào)函數(shù)實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10深入理解JavaScript系列(34):設(shè)計(jì)模式之命令模式詳解
這篇文章主要介紹了深入理解JavaScript系列(34):設(shè)計(jì)模式之命令模式詳解,命令模式(Command)的定義是:用于將一個(gè)請(qǐng)求封裝成一個(gè)對(duì)象,從而使你可用不同的請(qǐng)求對(duì)客戶進(jìn)行參數(shù)化,對(duì)請(qǐng)求排隊(duì)或者記錄請(qǐng)求日志,以及執(zhí)行可撤銷(xiāo)的操作,需要的朋友可以參考下2015-03-03如何在Web頁(yè)面上直接打開(kāi)、編輯、創(chuàng)建Office文檔
如何在Web頁(yè)面上直接打開(kāi)、編輯、創(chuàng)建Office文檔...2007-03-03js實(shí)現(xiàn)canvas圖片與img圖片的相互轉(zhuǎn)換的示例
本篇文章主要介紹了js實(shí)現(xiàn)canvas圖片與img圖片的相互轉(zhuǎn)換的示例,具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08將HTML格式的String轉(zhuǎn)化為HTMLElement的實(shí)現(xiàn)方法
本節(jié)主要介紹了將HTML格式的String轉(zhuǎn)化為HTMLElement的實(shí)現(xiàn)方法,需要的朋友可以參考下2014-08-08JS實(shí)現(xiàn)簡(jiǎn)易留言板增刪功能
這篇文章主要為大家詳細(xì)介紹了JS實(shí)現(xiàn)簡(jiǎn)易留言板增刪功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-02-02