最近最近作项目的时候总会思考一些大的应用设计模式相关的问题,我把本身的思考记录下来,供之后开发时参考,相信对其余人也有用。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; }; }