设计模式(1):只执行一次的函数

概述

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

使用utils

能够看到,上面的方法仍然很不优雅,要么用一个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

能用箭头函数的地方咱们都用了,可是为何咱们仍是捕获不了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;
  };
}

学到了什么

  1. 在vue里面能够用赋值的形式初始化方法,或者在created里面初始化方法。
  2. 箭头函数虽然能捕获this,但不是万能的;有时候咱们须要用function和apply结合来捕获this。
相关文章
相关标签/搜索