概述
最近最近做項目的時候總會思考一些大的應用設計模式相關的問題,我把自己的思考記錄下來,供以后開發時參考,相信對其他人也有用。
只執行一次的函數
我們經常會遇到這種情況,就是希望某個函數只執行一次,以后就不執行了。一般情況下,我們會這么寫:
<script>
export default {
data() {
return {
runOnce: true,
};
},
methods: {
func() {
console.log('hello world', this);
},
funcRunOnce() {
if (this.runOnce) {
this.func();
this.runOnce = false;
}
},
},
};
</script>
但是這樣並不優雅,不僅污染了data,還用2個方法進行實現,實在難看。
用閉包改進
於是我們考慮用閉包,把data里面的runOnce這個變量放到閉包里面去,這樣就不會污染data了。代碼如下:
<script>
export default {
methods: {
func() {
console.log('hello world', this);
},
funcRunOnce(params) {
let runOnce = true;
return () => {
if (runOnce) {
this.func();
runOnce = false;
}
}();
},
},
};
</script>
但是這么寫顯然是錯了,因為每次調用funcRunOnce都會構造一次閉包,里面的runOnce這個變量根本不會共享。所以繼續改寫如下:
// 方法1
<script>
export default {
created() {
this.funcRunOnce = this.runOnce(this.func);
},
methods: {
func() {
console.log('hello world', this);
},
runOnce(func) {
let runOnce = true;
return (params) => {
if (runOnce) {
func(params);
runOnce = false;
}
};
},
},
};
</script>
// 方法2
<script>
export default {
methods: {
func() {
console.log('hello world', this);
},
runOnce(func) {
let runOnce = true;
return (params) => {
if (runOnce) {
func(params);
runOnce = false;
}
};
},
funcRunOnce: this.runOnce(this.func),
},
};
</script>
使用utils
可以看到,上面的方法仍然很不優雅,要么用一個created和2個方法實現,要么用三個方法實現。而都用了一個公共的方法runOnce。所以我們考慮把runOnce放到utils.js里面去。
// utils.js
export function runOnce(func) {
let runOnce = true;
return (params) => {
if (runOnce) {
func(params);
runOnce = false;
}
};
}
//example.vue
import { runOnce } from '@/utils';
<script>
export default {
methods: {
funcRunOnce: runOnce(() => {
console.log('hello world', this);
}),
},
};
</script>
上面的寫法看起來非常簡潔,但是實際上是不行的,因為this的指向錯了。由於runOnce返回的函數並不是vue實例的方法,所以里面的this指向的是undefined。
注意:即使看起來我們好像在funcRunOnce方法中用箭頭函數捕獲了外面實例的this,但是實際上它捕獲的並不是外面的實例的this,而是runOnce返回的函數里面的this。
捕獲this
能用箭頭函數的地方我們都用了,但是為什么我們還是捕獲不了this呢?如此一來是不是完成不了這個任務了?
並不是,方法還是有的,方法是不用箭頭函數捕獲this。代碼如下:
// utils.js
export function runOnce(func) {
let runOnce = true;
return function(params) {
if (runOnce) {
func.apply(this, params);
runOnce = false;
}
};
}
//example.vue
import { runOnce } from '@/utils';
<script>
export default {
methods: {
funcRunOnce: runOnce(function h() {
console.log('hello world', this);
}),
},
};
</script>
通過查看代碼可以看出,2個地方的箭頭函數都被改寫成了function,並且還用到了apply函數來強制施加this。
理由很簡單,由於runOnce函數里面沒有用箭頭函數,所以它返回的函數是屬於vue實例的,所以它返回的函數的this,是指向vue實例的;又因為funcRunOnce里面沒有用箭頭函數,所以我們可以用apply把這個this強制附加到func里面去!
同理我們還可以寫出第一次不執行,后續才執行的函數:
// utils.js
// 第一次不執行,后續再執行
export function notRunOnce(func) {
let once = false;
return function(params) {
if (once) {
func.apply(this, params);
}
once = true;
};
}
學到了什么
- 在vue里面可以用賦值的形式初始化方法,或者在created里面初始化方法。
- 箭頭函數雖然能捕獲this,但不是萬能的;有時候我們需要用function和apply結合來捕獲this。