bug
,vue2的this指向問題
,使用了箭頭函數,導致拿不到對應的props
。當我給他介紹的時候他竟然不知道,隨後也刻意的看了一下前端交流群,至今最起碼還有70%以上的前端程式設計師搞不明白,今天給大家分享一下this
指向,如果啥都沒學會,請給我一個大嘴巴子。this
指向跟在哪裡定義無關,跟如何呼叫,通過什麼樣的形式呼叫有關this
(這個) 這個函數如何被呼叫(方便記憶) 上面我們介紹了,this
的指向主要跟通過什麼樣的形式呼叫有關。接下來我就給大家介紹一下呼叫規則,沒有規矩不成方圓,大家把這幾種呼叫規則牢記於心就行了,沒有什麼難的地方。
函數最常用的呼叫方式,呼叫函數的型別:獨立函數呼叫
function bar() { console.log(this) // window }
window
this
為undefined
用最通俗的話表示就是:物件擁有某個方法,通過這個物件存取方法且直接呼叫(注:箭頭函數特殊,下面會講解)
const info = { fullName: 'ice', getName: function() { console.log(this.fullName) } } info.getName() // 'ice'
info
發起呼叫,進行了隱式繫結,所以當前的this
為info
,通過this.fullName
毫無疑問的就存取值為ice
隱式丟失 普通
有些情況下會進行隱式丟失,被隱式繫結的函數會丟失繫結物件,也就是說它為變為預設繫結,預設繫結的this
值,為window
還是undefined
取決於您當前所處的環境,是否為嚴格模式。
const info = { fullName: 'ice', getName: function() { console.log(this.fullName) } } const fn = info.getName fn() //undefined
這種情況下就進行了隱式丟失,丟失了繫結的物件,為什麼會產生這樣的問題呢?如果熟悉記憶體的小夥伴,就會很容易理解。
info
找到了對應getName
的記憶體地址,賦值給變數fn
fn
直接進行了呼叫window
,從window
中取出fullName
屬性,必定為undefined
隱式丟失 進階
這裡大家首先要理解什麼是回撥函數。其實可以這樣理解,就是我現在不呼叫它,把他通過引數的形式傳入到其他地方,在別的地方呼叫它。
//申明變數關鍵字必須為var var fullName = 'panpan' const info = { fullName: 'ice', getName: function() { console.log(this.fullName) } } function bar(fn) { //fn = info.getName fn() // panpan } bar(info.getName)
bar
中的fn
為一個回撥函數fn = info.getName
引數傳遞就是一種隱式賦值,其實跟上面的隱式丟失是一個意思,他們都是指向的fn = info.getName
參照,也就是它們的記憶體地址this
丟失,也就是函數獨立呼叫,預設繫結規則,this
為全域性的window
物件var
呢?var
申明的變數才會加入到全域性window
物件上let\const
則不是,具體的後續介紹一下這兩個申明變數的關鍵字 但是在某些場景下,this
的改變都是意想不到的,實際上我們無法控制回撥函數的執行方式,因此沒有辦法控制呼叫位置已得到期望的繫結即this指向。
接下來的顯示繫結就可以用來解決這一隱式丟失問題。
js中的 」所有「函數都有一些有用的特性,這個跟它的原型鏈有關係,後續我會在原型介紹,通過原型鏈js中變相實現繼承的方法,其中call/apply/bind
這三個方法就是函數原型鏈上的方法,可以在函數中呼叫它們。
call()
方法使用一個指定的 this
值和單獨給出的一個或多個引數來呼叫一個函數。this
物件apply()
方法類似,只有一個區別,就是 call()
方法接受的是一個參數列,而 apply()
方法接受的是一個包含多個引數的陣列。var fullName = 'panpan' const info = { fullName: 'ice', getName: function(age, height) { console.log(this.fullName, age, height) } } function bar(fn) { fn.call(info, 20, 1.88) //ice 20 1.88 } bar(info.getName)
call
的方法類似,只是參數列有所不同call
引數為單個傳遞apply
引數為陣列傳遞var fullName = 'panpan' const info = { fullName: 'ice', getName: function(age, height) { console.log(this.fullName, age, height) } } function bar(fn) { fn.apply(info, [20, 1.88]) //ice 20 1.88 } bar(info.getName)
bind
與apply/call
之間有所不同,bind
傳入this
,則是返回一個this
繫結後的函數,呼叫返回後的函數,就可以拿到期望的this。bind
時,可以傳入引數bind
返回的引數也可以進行傳參var fullName = 'panpan' const info = { fullName: 'ice', getName: function(age, height) { console.log(this.fullName, age, height) //ice 20 1.88 } } function bar(fn) { let newFn = fn.bind(info, 20) newFn(1.88) } bar(info.getName)
談到new
關鍵字,就不得不談建構函式,也就是JS中的 "類",後續原型篇章在跟大家繼續探討這個new關鍵字,首先要明白以下幾點,new Fn()
的時候發生了什麼,有利於我們理解this
的指向。
建立了一個空物件
將this指向所建立出來的物件
把這個物件的[[prototype]] 指向了建構函式的prototype屬性
執行程式碼塊程式碼
如果沒有明確返回一個非空物件,那麼返回的物件就是這個建立出來的物件
function Person(name, age) { this.name = name this.age = age } const p1 = new Person('ice', 20) console.log(p1) // {name:'ice', age:20}
new Person()
的時候,那個this所指向的其實就是p1
物件function bar() { console.log(this) //info } const info = { bar: bar } info.bar()
widonw或者undefined
,變相的可以認為隱式繫結 > 預設繫結var fullName = 'global ice' const info = { fullName: 'ice', getName: function() { console.log(this.fullName) } } info.getName.call(this) //global ice info.getName.apply(this) //global ice info.getName.bind(this)() //global ice
function bar() { console.log(this) //123 } const newFn = bar.bind(123) newFn.call(456)
首先我們來說一下,為什麼是和bind
比較,而不能對call
和apply
比較,思考下面程式碼
const info = { height: 1.88 } function Person(name, age) { this.name = name this.age = age } const p1 = new Person.call('ice', 20) //報錯: Uncaught TypeError: Person.call is not a constructor
new繫結和bind繫結比較
const info = { height: 1.88 } function Person(name, age) { this.name = name this.age = age } const hasBindPerson = Person.bind(info) const p1 = new hasBindPerson('ice', 20) console.log(info) //{height: 1.88}
bind
對Person
進行了一次劫持,硬繫結了this為info
物件new
返回的固定this的函數new關鍵字
> bind
> apply/call
> 隱式繫結
> 預設繫結
首先箭頭函數是ES6
新增的語法
const foo = () => {}
var fullName = 'global ice' const info = { fullName: 'ice', getName: () => { console.log(this.fullName) } } info.getName() //global ice
ice
ES6
的新特性,箭頭函數不繫結this
,它的this
是上一層作用域,上一層作用域為window
global ice
getObjName
通過this
拿到info
中的fullName
(值為ice
的fullName
)const info = { fullName: 'ice', getName: function() { let _this = this return { fullName: 'panpan', getObjName: function() { console.log(this) // obj console.log(_this.fullName) } } } } const obj = info.getName() obj.getObjName()
當我呼叫 info.getName()
返回了一個新物件
當我呼叫返回物件的getObjName
方法時,我想拿到最外層的fullName
,我通過,getObjName
的this存取,拿到的this卻是obj
,不是我想要的結果
我需要在呼叫info.getName()
把this儲存下來,info.getName()
是通過隱式呼叫,所以它內部的this就是info物件
getObjName
是obj物件,因為也是隱式繫結,this必定是obj物件,繞了一大圈我只是想拿到上層作用域的this而已,恰好箭頭函數解決了這一問題
const info = { fullName: 'ice', getName: function() { return { fullName: 'panpan', getObjName: () => { console.log(this.fullName) } } } } const obj = info.getName() obj.getObjName()
預設繫結
隱式繫結
顯示繫結 apply/call/bind(也稱硬繫結)
new繫結
new繫結
bind
call/apply
隱式繫結
預設繫結
當一切都看起來不起作用的時候,我就會像個石匠一樣去敲打石頭,可能敲100次,石頭沒有任何反應,但是101次,石頭可能就會裂為兩半 我知道並不是第101次起了作用,而是前面積累所致。
大家有疑惑可以在評論區留言 第一時間為大家解答。
(學習視訊分享:)
前端(vue)入門到精通課程: