• 设计模式(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。
  • 相关阅读:
    我的Git教程 之 初始化本地库
    再学Java 之 Integer 包装类缓存
    再学Java 之 private、protected、public和default的作用域
    再学Java 之 形参个数可变函数
    再学Java 之 foreach循环
    解决MySQL中文乱码问题
    再学Java 之 解决No enclosing instance of type * is accessible
    从一个例子学习 instanceof 和 getclass 的区别
    如何单独启动wamp 中自带的MySQL
    网络游戏程序员须知 UDP vs TCP(转)
  • 原文地址:https://www.cnblogs.com/yangzhou33/p/11204180.html
Copyright © 2020-2023  润新知