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