JavaScript中this的綁定你知道幾種?
執(zhí)行上下文
我們知道執(zhí)行上下文分為兩種:全局上下文和函數(shù)上下文(我的這篇文章對于執(zhí)行上下文有講解還對執(zhí)行上下文和作用域迷糊嗎?)。全局上下文只有一個(gè),函數(shù)執(zhí)行上下文是在函數(shù)調(diào)用的時(shí)候創(chuàng)建的。
每個(gè)執(zhí)行上下文都有三個(gè)屬性:
- 變量對象
- 作用域鏈
- this
this到底是什么呢
this是在運(yùn)行時(shí)綁定的,并不是在編寫時(shí)綁定的,它的上下文取決于函數(shù)調(diào)用時(shí)的各種條件。this的綁定和函數(shù)聲明的位置沒有任何的關(guān)系,只取決于函數(shù)的調(diào)用方式。
調(diào)用位置
要理解this的綁定過程,首先要理解調(diào)用位置。調(diào)用位置就是函數(shù)的調(diào)用的位置(不是聲明的位置)。所以我們要先來分析調(diào)用棧(也就是執(zhí)行上下文棧)。我們先來看一段代碼。
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, // 全局 ]
通過調(diào)用棧我們就可以很清晰的找到函數(shù)的調(diào)用位置。baz在全局調(diào)用,bar在baz里調(diào)用,foo在bar里調(diào)用。
那函數(shù)在執(zhí)行的時(shí)候是如何決定this的綁定對象的呢?
綁定規(guī)則
通過綁定規(guī)則決定this的綁定對象。
默認(rèn)綁定
最常用的調(diào)用類型:獨(dú)立函數(shù)調(diào)用。
function foo(){ console.log(this.a) // 2 } var a = 2; foo()
函數(shù)調(diào)用的時(shí)候,使用了this的默認(rèn)綁定,因此this指向全局對象。
那么我們怎么知道這里應(yīng)用了默認(rèn)綁定呢?可以通過分析調(diào)用位置來看看 foo() 是如何調(diào)用的。在代碼中,foo()是直接使用不帶任何修飾的函數(shù)引用進(jìn)行調(diào)用的,因此只能使用默認(rèn)綁定,無法應(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對象所包含的,落腳點(diǎn)指向obj對象。當(dāng)函數(shù)引用有上下文對象時(shí),隱式綁定規(guī)則會把函數(shù)調(diào)用中的this綁定到這個(gè)上下文對象。
通過一個(gè)對象調(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)綁定。
我們再看另一種情況
function foo() { console.log(this.a) } function doFoo(fn) { fn() } var obj = { a: 2, foo } var a = "mick" doFoo(obj.foo) // mick
嵌套函數(shù)中的this 不會從外層函數(shù)中繼承。this永遠(yuǎn)指向最后調(diào)用它的那個(gè)對象
顯示綁定
可以使用call、apply或bind方法。如果對這三個(gè)方法的實(shí)現(xiàn)原理感興趣可以看看這篇手寫call、apply、bind
function foo() { console.log(this.a) } var obj = { a: 2 } foo.call(obj) // 2
通過call方法,可以在調(diào)用foo時(shí)候,強(qiáng)制把它的this綁定到obj上。
new綁定
這里我們先說一下new來調(diào)用函數(shù)會發(fā)生哪些事情
- 創(chuàng)建一個(gè)全新的對象
- 這個(gè)新對象會被執(zhí)行[[原型]]鏈接
- 這個(gè)新對象會綁定到函數(shù)調(diào)用的this
- 如果函數(shù)沒有返回其他對象,那么new表達(dá)式中的函數(shù)調(diào)用會自動返回這個(gè)新對象。
function foo(a){ this.a = a } var bar = new foo(2) console.log(bar.a)
使用new來調(diào)用foo時(shí),我們會構(gòu)造一個(gè)新對象并把它綁定到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è)來解析一下。
person.foo1()
這個(gè)屬于隱式綁定,foo1的this綁定到了person,所以打印person1person1.foo1.call(person2)
顯示綁定,foo1的this通過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)()
通過call只是改變了foo3的this指向,和返回的函數(shù)沒有什么關(guān)系,所以this還是指向window,打印windowperson1.foo3().call(person2)
通過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通過call改變成了person2,所以打印personperson1.foo4().call(person2)
這個(gè)和person1.foo4()
是一樣的道理,打印person1
簡單的談了談this的綁定,歡迎留言你的問題,大家一起學(xué)習(xí)一起進(jìn)步?。。?/p>
到此這篇關(guān)于JavaScript中this的綁定你知道幾種?的文章就介紹到這了,更多相關(guān)JavaScript this綁定內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 一文全面解析JS中的this綁定規(guī)則
- JavaScript中this綁定規(guī)則你理解了嗎
- 細(xì)說JavaScript中的this指向與綁定規(guī)則
- JavaScript this綁定與this指向問題的解析
- JavaScript?中的?this?綁定規(guī)則詳解
- 詳解JavaScript中的this硬綁定
- 一文搞懂JavaScript中的this綁定規(guī)則
- JavaScript中?this?的綁定指向規(guī)則
- 詳解JavaScript的this指向和綁定
- JavaScript this綁定過程深入詳解
- React.js綁定this的5種方法(小結(jié))
- JavaScript調(diào)用模式與this關(guān)鍵字綁定的關(guān)系
- 深入理解JavaScript this綁定規(guī)則
相關(guān)文章
關(guān)于javascript中偽數(shù)組和真數(shù)組的一些小秘密
在javascript中,偽數(shù)組又稱類數(shù)組,是一個(gè)類似數(shù)組的對象,是一種按照索引存儲數(shù)據(jù)且具有l(wèi)ength屬性的對象,下面這篇文章主要給大家介紹了關(guān)于javascript中偽數(shù)組和真數(shù)組的一些小秘密,需要的朋友可以參考下2022-08-08頁面間固定參數(shù),通過cookie傳值的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄撁骈g固定參數(shù),通過cookie傳值的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-056種JavaScript判斷對象自身為空的方法小結(jié)
這篇文章主要為大家詳細(xì)介紹了6種JavaScript判斷對象自身為空的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-12-12比較詳細(xì)的javascript對象的property和prototype是什么一種關(guān)系
比較詳細(xì)的javascript對象的property和prototype是什么一種關(guān)系...2007-08-08JS+HTML5本地存儲Localstorage實(shí)現(xiàn)注冊登錄及驗(yàn)證功能示例
這篇文章主要介紹了JS+HTML5本地存儲Localstorage實(shí)現(xiàn)注冊登錄及驗(yàn)證功能,結(jié)合實(shí)例形式分析了基于JS+HTML5本地存儲Localstorage實(shí)現(xiàn)注冊登錄及驗(yàn)證相關(guān)操作技巧,需要的朋友可以參考下2020-02-02JS實(shí)現(xiàn)將對象轉(zhuǎn)化為數(shù)組的方法分析
這篇文章主要介紹了JS實(shí)現(xiàn)將對象轉(zhuǎn)化為數(shù)組的方法,結(jié)合實(shí)例形式分析了javascript操作及轉(zhuǎn)換json數(shù)組相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-01-01如何用RxJS實(shí)現(xiàn)Redux Form
這篇文章主要介紹了如何用RxJS實(shí)現(xiàn)Redux Form,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12