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

概述

最近最近做项目的时候总会思考一些大的应用设计模式相关的问题,我把自己的思考记录下来,供以后开发时参考,相信对其他人也有用。

只执行一次的函数

我们经常会遇到这种情况,就是希望某个函数只执行一次,以后就不执行了。一般情况下,我们会这么写:

<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;
  };
}

学到了什么

  1. 在vue里面可以用赋值的形式初始化方法,或者在created里面初始化方法。
  2. 箭头函数虽然能捕获this,但不是万能的;有时候我们需要用function和apply结合来捕获this。

猜你喜欢

转载自www.cnblogs.com/yangzhou33/p/11204180.html