作者:Michael Thiessen 譯者:前端小智 來源:techalyst
點贊再看,養成習慣
本文
GitHub
github.com/qq449245884… 上已經收錄,更多往期高贊文章的分類,也整理了很多我的文件,和教程資料。歡迎Star和完善,大家面試可以參照考點複習,希望我們一起有點東西。
當我們使用 Vue 在愉快的開發專案的時候,突然報了一個錯誤:
this is undefined
複製程式碼
別擔心,不只有你一個人,我也經常遇到這個問題很多次,接下我們一起來看看如何解決這個問題。
一個可能的原因是混淆了常規函式和箭頭函式的用法,如果你遇到這個問題,我猜你用的是箭頭函式。如果用常規函式替換箭頭函式,它可能會為你修復這個問題。
我們再深入一點,試著理解為什麼會這樣。
畢竟,知識就是力量,如果知道造成問題的原因,那麼我們將來可以避免很多挫敗感和時間浪費。
還有一些其它原因可能也會出現此類錯誤。
- 使用
fetch
或axios
獲取資料 - 使用像
lodash
或underscore
這類的庫
理解兩種主要的函式型別
在 JS 中,我們有兩種不同的函式。它們以幾乎相同的方式運作,除了它們處理變數的方式不同。
這給新舊Javascript開發人員帶來了很多困惑,但是當我們弄懂這個問題時,就很好會有這個困惑。
常規函式
常規函式可以用幾種不同的方式定義。
第一種方法在 Vue 元件中較不常見,因為寫出來要更長一些:
methods: {
regularFunction: function() {
// Do some stuff
}
}
複製程式碼
第二種方法是簡寫方式,我們也經常使用:
methods: {
shorthandFunction() {
// Do some stuff
}
}
複製程式碼
在像這樣的常規函式中,this
將引用函式的“所有者”。因為我們是在Vue元件上定義它的,所以this
指的是Vue元件。
在大多數情況下,我們應該在 Vue 中使用常規函式,特別是在建立時
- methods
- computed props
- watched props
雖然常規函式通常是我們所需要的,但是箭頭函式也非常方便。
箭頭函式
箭頭函式可以更短,更快的編寫,因此最近獲得了廣泛的歡迎。但是,它們在物件上定義方法時並沒有太大的不同,就像我們在編寫Vue元件時所做的那樣。
這是他們在Vue元件上的樣子:
methods: {
arrowFunction: () => {
// Do some stuff
}
}
複製程式碼
在處理 this
問題時,真正的差異開始發揮作用。
箭頭函式採用詞法作用域,意味著箭頭函式從它的上下文中獲取this
。
如果試圖從Vue元件上的箭頭函式內部訪問 this
,將得到一個錯誤,因為 this
不存在
data() {
return {
text: 'This is a message',
};
},
methods: {
arrowFunction: () => {
console.log(this.text); // ERROR! this is undefined
}
}
複製程式碼
簡而言之,儘量避免在Vue元件上使用箭頭函式。這將會省去許多頭痛和困惑的問題。
有時使用箭頭函式是很好的,但這隻在不引用this
的情況下才有效。
computed: {
location: () => window.location,
}
複製程式碼
現在我們知道兩種主要的函式型別,如何正確使用它們?
匿名函式
當我們只需要建立一個函式而不需要從其他任何地方呼叫它時,匿名函式非常有用。
下面是使用匿名函式的一些場景
- 使用
axios
或fetch
訪存資料 filter
、map
和reduce
等函式方法- 在 Vue 方法中的任何地方
來個例子看一下:
// Fetching data
fetch('/getSomeData').then((data) => {
this.data = data;
});
// Functional methods
const array = [1, 2, 3, 4, 5];
const filtered = array.filter(number => number > 3);
const mapped = array.map(number => number * 2);
const reduced = array.reduce((prev, next) => prev + next);
複製程式碼
從示例中可以看到,大多數情況下,當我們建立匿名函式時,使用箭頭函式。我們通常使用箭頭函式有幾個原因
- 更短、更簡潔的語法
- 改善可讀性
- this 取自父類
在Vue方法中,箭頭函式也可以作為匿名函式使用。
等等,我們不是剛發現當我們試圖訪問 this
時,箭頭函式不起作用嗎?
這就是區別所在。
當我們在常規函式或簡寫函式中使用箭頭函式時,常規函式將this
設定為我們的Vue元件,而箭頭函式則不一樣。
來個例子:
data() {
return {
match: 'This is a message',
};
},
computed: {
filteredMessages(messages) {
console.log(this); // Vue
const filteredMessages = messages.filter(
// 引用我們的Vue元件
(message) => message.includes(this.match)
);
return filteredMessages;
}
}
複製程式碼
filter
方法可以訪問this.match
,因為箭頭函式使用的方法與filteredMessages
方法使用的上下文相同。 由於此方法是常規函式(而不是箭頭函式),因此將其自身的上下文設定為Vue例項。
讓我們進一步討論如何使用axios
或fetch
來獲取資料。
在獲取資料時使用正確的函式
如果正在使用fetch
或axios
獲取非同步資料,最好使用 Promise
。Promise
喜歡匿名箭頭函式,它們也使處理this
問題變得容易得多。
如果你正在獲取一些資料並想在你的元件上設定它,這是你應該做的正確的方式:
export default {
data() {
return {
dataFromServer: undefined,
};
},
methods: {
fetchData() {
fetch('/dataEndpoint')
.then(data => {
this.dataFromServer = data;
})
.catch(err => console.error(err));
}
}
};
複製程式碼
請注意,我們如何在 Vue 元件上使用常規函式作為方法,然後在 Promise 內部使用匿名箭頭函式
.then(data => {
this.dataFromServer = data;
})
複製程式碼
在fetchData()
作用域內,我們將this
設定為Vue元件,因為它是一個常規函式。由於箭頭函式使用外部作用域作為它們自己的作用域,因此箭頭函式也將this
設定為我們的Vue元件。
這允許我們通過this
訪問 Vue 元件並更新dataFromServer
。
但是,如果需要將函式傳遞幫助庫,比如lodash
或underscore
,該怎麼辦呢
與 Lodash 或 Underscore 一起使用
假設我們的Vue元件上有一個要使用Lodash
或Underscore
方法。如何防止this is undefine
的錯誤。
如果你用過 React ,你可能見過類似的東西。
這是我們用Vue做的。
created() {
this.methodToDebounce = _.debounce(this.methodToDebounce, 500);
},
methods: {
methodToDebounce() {
// Do some things here
}
}
複製程式碼
就是這樣!
我們要做的就是獲取函式,將其包裝在debounce
函式中,然後返回一個內建了debounce
的新函式。現在,當我們在Vue元件上呼叫this.methodToDebounce()
時,我們將呼叫debounced
版本。
什麼是詞法作用域
如前所述,常規函式和箭頭函式之間存在差異的主要原因與詞法作用域有關。來分析一下它的含義。
首先,作用域是程式中存在變數的任何區域。在Javascript中,window 變數具有全域性作用域,它在任何地方都可用。儘管大多數變數被限制在定義它們的函式、它們所屬的類或模組中。
其次,單詞“詞法”僅僅意味著作用域由你如何編寫程式碼決定。某些程式語言只在程式執行時才確定作用域內的內容。這可能會讓人很困惑,所以大多數語言都只使用詞法作用域。
箭頭函式使用詞法作用域,而常規函式和簡寫函式不使用。
這裡最棘手的部分是詞法作用域如何在函式中影響 this
。對於箭頭函式,this
與外部作用域的this
繫結在一起。常規函式的this
繫結方式有些奇怪,這就是引入箭頭函式的原因,也是為什麼大多數人儘可能多地使用箭頭函式的原因。
作用域如何在函式中工作
下面是一些示例,它們演示了作用域如何在這兩種函式型別之間以不同的方式工作
// 此變數在 window 作用域內
window.value = 'Bound to the window';
const object = {
// 此變數在 object 作用域內
value: 'Bound to the object',
arrowFunction: () => {
console.log(this.value); // 'Bound to the window'
},
regularFunction() {
console.log(this.value); // 'Bound to the object'
}
};
複製程式碼
將作用域繫結到函式上
我們可以使用 bind 方法來改變 this
的繫結
const boundFunction = unboundFunction.bind(this);
複製程式碼
這使我們在編寫Vue元件時具有更大的靈活性,更輕鬆地重用方法。當然,可讀性相對差點,應該儘量避免太頻繁地使用它。
程式碼部署後可能存在的BUG沒法實時知道,事後為了解決這些BUG,花了大量的時間進行log 除錯,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug。
原文: www.techalyst.com/posts/vuejs…
交流
文章每週持續更新,可以微信搜尋「 大遷世界 」第一時間閱讀和催更(比部落格早一到兩篇喲),本文 GitHub github.com/qq449245884… 已經收錄,整理了很多我的文件,歡迎Star和完善,大家面試可以參照考點複習,另外關注公眾號,後臺回覆福利,即可看到福利,你懂的。