this全面解析, 如何定位this指向,一文总结,再也不怕面试官追问啦
一、調用位置
在理解this的綁定過程之前,首先要理解調用位置:調用位置就是函數在代碼中被調用的位置(而不是申明的位置)。只有仔細分析調用位置才能回答這個問題:這個this到底引用的是什么?
function foo() {console.log('foo')// 當前調用棧是: bar// 因此,當前調用位置在bar中 }function bar() {console.log('bar')// foo當前調用位置foo()// 當前調用棧是: bar// 因此,當前調用位置在bar中 }function baz() {console.log('baz')// bar當前調用位置bar()// 當前調用棧是: baz// 因此,當前調用位置是全局作用域 }// baz當前調用位置 // 全局作用域 baz()二、綁定規則
1、默認綁定(非嚴格模式):獨立函數調用
const a = 2 function foo() {console.log(this.a) } foo() // 2 // foo() 在這里直接食用不帶任何修飾的函數引用進行調用,因此只能使用默認綁定;調用位置為全局作用域,因此this默認綁定到全局作用域。2、隱式綁定:調用位置是否有上下文,或者說是否被某個對象擁有或者包含
- 當函數引用有上下文對象時,隱式綁定規則會把函數調用中this綁定到這個上下文對象上。
對象屬性引用鏈中只有 最頂層 或者說 最后一層 會影響調用位置
function foo() {console.log(this.a) } const obj1 = {a: 2,foo: obj2 } const obj2 = {a: 42,foo: foo } obj1.obj2.foo() // 42隱式丟失:一個最常見的this綁定問題就是 被隱式綁定的函數 會丟失綁定對象,也就是說它會引用默認綁定,從而把this綁定到全局對象或者undefined上,取決于是否是嚴格模式。
function foo() {console.log(this.a) } const obj = {a: 2,foo: foo } const bar = obj.foo // 函數別名! const a = '我是全局對象' bar() // -> 我是全局對象 // 雖然bar是obj.foo的一個引用,但是實際上,他引用的是foo函數本身,因此此時的 bar() 相當于 foo(), 是一個不帶任何修飾的函數調用,因此應用了默認綁定 function foo() {console.log(this.a) } function doFoo(fn) {// fn其實引用的是foofn() // <--調用位置! } const obj = {a: 2,foo: foo } const a = '我是全局對象' doFoo(foo) // -> 我是全局對象 // 參數傳遞其實就是一種隱式賦值,因此我們傳入函數時也會被隱式賦值,所以結果和上一個例子一樣 // 如果把函數傳入語言內置的函數而不是自定義的函數,會怎么樣吶?結果是一樣的,沒有區別 function foo() {console.log(this.a) } const obj = {a: 2,foo: foo } const a = '我是全局對象' setTimeout(obj.foo, 100) // -> 我是全局對象// JavaScript環境中內置的setTimeout()函數實現和下面的代碼類似: function setTimeout(fn, delay) {// 等待delay毫秒fn() // <--調用位置! }3、顯式綁定
function foo() {console.log(this.a) } const obj = {a: 2, } foo.call(obj) // -> 2 // 通過foo.call(...)可以在調用foo時強制把它綁定到obj上// 思考下面代碼輸出什么 const b = '我是全局對象' function foo() {console.log(this.b) } const obj = {b: 2,foo: foo } const bar = function(fn) {fn() } bar.call(obj, foo) // -> 我是全局對象// 顯然,通過這種方式還是無法解決綁定丟失問題3.1、硬綁定
function foo() {console.log(this.b) } const obj = {b: 2, } const bar = function() {foo.call(obj) } bar() // -> 2 // 硬綁定的bar不能再修改它的this bar.call(window) // -> 2由于硬綁定是一種非常常用的模式。所以在es5中提供了內置的方法Funtion.prototype.bind
bind(…)會返回一個硬編碼的新函數,它會吧參數設置為this的上下文并調用原始函數
4、new綁定
使用new來調用函數,或者說發生構造函數調用時,它會自動執行下面的操作:
三、優先級
如果某個調用位置同時使用四條規則,那誰的優先級更高呢?
1、隱式綁定與顯式綁定誰的優先級更高?
2、隱式綁定與new綁定誰的優先級更高?
function foo(something) {this.a = something } const obj1 = {foo: foo } const obj2 = {}obj1.foo(2) console.log(obj1.a) // -> 2obj1.foo.call(obj2, 3) console.log(obj2.a) // -> 3const bar = new obj1.foo(4) console.log(obj1.a) // -> 2 console.log(bar.a) // -> 4// 可以看到,new綁定的優先級高于隱式綁定3、new綁定與顯式綁定誰的優先級更高?
new和call/apply無法一起使用,因此無法通過 new foo.call(obj1)來直接進行測試,但是我們可以使用硬綁定來進行測試
四、判斷this步驟
現在我們可以根據優先級來判斷函數在某個調用位置引用的式哪條規則:
const bar = new foo()
const bar = foo.call(obj2)
const bar = obj1.foo()
const bar = foo()
總結
以上是生活随笔為你收集整理的this全面解析, 如何定位this指向,一文总结,再也不怕面试官追问啦的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 修改Maven本地仓库的位置 方法
- 下一篇: 第八篇:Spring Boot整合Thy