本人所有文章首發在部落格園: www.cnblogs.com/zhangrunhao…
問題背景
需求描述
- 在路由的導航欄中需要, 判斷是否為第一次點選
- 需要一個標誌位來記錄是否點選過
- 現狀:
- 這個標誌位只在一個函式中用過.不希望存放全域性
- 希望在這個methods中形成閉包, 用來快取這個函式
- 做出如下嘗試後, 發現可以實現.
- 當前問題:
- 不能在閉包呼叫時找到正確的this.
詭異點
- 測試使用時: 返回的this找到了window
// 測試使用:
<div id="app">
<button @click="test">測試按鈕</button>
</div>
<script>
var app = new Vue({
el: '#app',
methods: {
test: (() => {
`use strict`
console.log(this) // Window
var flag = true
return () => {
console.log(this) // Window
flag = false
}
})()
}
})
</script>
複製程式碼
-
實際專案中的this變成了
undefined
-
更加詭異的是
debugger
之後, 我們一步步來看 -
當前程式碼:
pointJump: (() => {
let isFirstChanged = false;
console.log(this);
debugger;
return entry => {
console.log(this);
console.log(isFirstChanged);
debugger;
isFirstChanged = true;
};
})(),
複製程式碼
- 操作:
- 重新整理頁面, 第一次函式立即執行
- 頁面生成完成後: 我們再次通過按鈕觸發事件: 此時debugger顯示記憶體中為Vue的頂級物件, 而在控制檯列印出來的依舊是undefined
執行過程分析
- 第一次執行的時候為undefined是正常的, 因為第一次閉包執行, 沒有找到this
- 當我們再次執行的時候, 雖然呼叫起來的上下文, 也就是this已經改了, 但是因為在作用域中那個
this
所代表的空間還是undefined
, 所以沒有能改變過來. - 就造成了我們所看到的詭異的現象.
與測試檔案有差別的原因
- 因為在測試環境下, 沒有能開啟嚴格模式.
- 經過兩次不同位置的的開啟嘗試, 都不對
- 依舊可以找到window物件
- 現在推測是在vue內部進行的實現, 因為引入的vue版本不同.需要再進行測試, 看來原始碼還是要好好過一遍
<script>
var app = new Vue({
el: '#app',
methods: {
test: (() => {
`use strict`
console.log(this) // Window
var flag = true
return () => {
console.log(this) // Window
flag = false
}
})()
}
})
</script>
複製程式碼
最後找到原因的測試
- 因為箭頭函式的this是不會改變, 擁有根據父級能夠返回的this
- 然後因為上面的閉包環境中的this, 指向的一直都是
undefined
const test = (() => {
let aaa = true;
return function () {
console.log(this);
aaa = false;
};
})();
mainJump(entry) {
test.call(this);
},
複製程式碼
解決方法
- 形成閉包返回的函式中, 不要使用箭頭函式, 使用function定義即可
pointJump: (() => {
let isFirstChanged = false;
return function () {
console.log(this); // Vue的頂級物件
isFirstChanged = true;
};
})(),
複製程式碼
總結
- 箭頭函式不會被call, bind等方法改變this指向
- 在閉包中返回函式, 快取變數時, 使用function進行返回函式的定義.