最近最近作項目的時候總會思考一些大的應用設計模式相關的問題,我把本身的思考記錄下來,供之後開發時參考,相信對其餘人也有用。vue
咱們常常會遇到這種狀況,就是但願某個函數只執行一次,之後就不執行了。通常狀況下,咱們會這麼寫:設計模式
<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了。代碼以下:app
<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>
能夠看到,上面的方法仍然很不優雅,要麼用一個created和2個方法實現,要麼用三個方法實現。而都用了一個公共的方法runOnce。因此咱們考慮把runOnce放到utils.js裏面去。this
// 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。code
能用箭頭函數的地方咱們都用了,可是爲何咱們仍是捕獲不了this呢?如此一來是否是完成不了這個任務了?ip
並非,方法仍是有的,方法是不用箭頭函數捕獲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; }; }