笨鸟学习日记之get封装函数的一点小技巧

282 阅读4分钟

单例封装思想以及任务队列的hack方式

一、 单例封装思想

1.背景

我们经常在项目过程中会使用很多的工具函数,我们通常会用一个类进行封装,使用时,只需将工具文件进行import进入我们的代码中,实例化工具类即可使用封装的函数。

2.代码封装的过程

  1. 首先,假设我们现在需要进行两个数字的求和运算,并且多次在代码中调用,此时我们会在文件中生命一个函数add()。
function add (num1,num2) {
  return num1 + num2;
}
  1. 其次,随着项目的开发和业务的增多,有个需求需要我们进行两个数字的求和运算,并且调用的频率很高,同样我们也可以封装一个函数出来。
function sub (num1,num2) {
  return num1 - num2;
}
  1. 之后的开发过程中可能有许许多多的这种工具函数,所以我们最好封装起来,使用ES6语法,class的形式进行封装会更加直观。

    class Utils {
      add(num1,num2) {
        return num1 + num2;
      }
      
      sub(num1,num2) {
      return num1 - num2;
      }
    }
    
  2. 这样有个问题每次使用的时候都需要new这个utils类,当我们使用vue进行组件化开发的时候,就会产生一个问题,每个需要使用的.vue文件都需要new一下我们的工具包,这个时候我们可以直接挂载到main.js中的vue原型对象中去,使得改utils类的实例对象成为一个全局的对象,但是当我们的项目做大的时候,会有好几个工具类进行封装,这时候如果我们全部挂载到vue的原型对象上去容易出现命令冲突或者代码整洁度低,维护起来困难等问题。这时候我们可以使用单例模式的设计思想进行封装。就是在类里面实例化自己存在自己的静态成员(静态属性)中去,向外面暴露出一个getIntance()方法,从而拿到这个实例对象。接下来让我们看代码的实现部分:

class Utils {
  static _instance;
  constructor(){};
  static getInstance() {
    if(!this._instance) {
      this._instance = new Utils();
    }
    return this._instance;
  }
  add(num1,num2) {
    return num1 + num2;
  }
  
  sub(num1,num2) {
  return num1 - num2;
  }
}
  1. 我们使用的时候就非常的方便,但是要主要我们业务的使用场景,需要使用实例化工具类的时候就用new关键字进行声明。平时的使用场景例如:
// HelloWorld.vue
import {Utils} from 'utils/utils'
export default {
  methods:{
    calculate(num1,num2) {
      return Utils.getIntance().add(num1,num2) +  
        Utils.getInstance().sub(num1,num2);
    }
  }
}

二、任务队列的Hack方式

1.使用场景

最近,在公司的项目中有一个需求,是这样的,有一个轮询的任务队列需要进行场景的判断,我把它抽象出来。就是在while(true)条件下不断去执行循环体内的代码,每次执行的时候需要将代码的执行暂停住,这时候我们在内部判断是否已经到达跳出循环的条件,如果是,则我们就退出循环。

2.代码封装的过程

  1. 首先是,我们假设需要轮询的任务是一个简单自增的计算器。
function counter(maxNum) {
  let count = 0;
  while(true) {
    counter++;
    sleep() // 这是一个暂停while的向下执行的函数
    if(count >= maxNum) return;
  }
}
  1. 这样写的好处就是我们可以在sleep()函数中修改count的值,可以随时终止轮询的进行。在我们Javascript中,可以这样子写,使用异步函数配合await关键字达到暂停代码的向下执行的一个效果。
function sleep() {
  new Promise((res,rej) => {
    // 这个定时器在这里的效果是开启一个异步函数去执行它,从而配合await使用
    setTimeout(() => {
      // doSomething()...这里还可以进行操作,从而终止任务队列的执行
    },0)
  })
}
async function counter(maxNum) {
  let count = 0;
  while(true) {
    counter++;
    await sleep() // 这是一个暂停while的向下执行的函数
    if(count >= maxNum) return;
  }
}
  1. 思路大概是这个样子,我们还用类包裹起来,我们先做一个简单的Demo。
class Counter {
  count;
  flag;
  constructor(){
    this.count = 0;
    this.flag = true;
  }
  sleep() {
    return new Promise((res,rej) => {
      // 这个定时器在这里的效果是开启一个异步函数去执行它,从而配合await使用
      setTimeout(() => {
        // doSomething()...这里还可以进行操作,从而终止任务队列的执行
        if(this.count > 1000) {
          this.flag = false;
        }
     },0)
   })
  }
  async calculate(maxNum) {
    while(this.flag && this.count < maxNum) {
      this.count++;
     await this.sleep() // 这是一个暂停while的向下执行的函数
    }
    
   // 这里我们判断一下是什么原因导致while循环结束的
    if (this.count >= maxNum) {
            console.log('calculated: count, count: ', this.count, ' flag: ', this.flag)
        }
​
    if (!this.flag) {
            console.log('calculated: flag, count: ', this.count, ' flag: ', this.flag)
        }
  }
}
  1. 结果分析

    测试结果如下图:

image-20210723100825410.png 可以很直观的看到我们在sleep中终止了while循环,还可以暴露出去一个cancel方法用来控制while循环条件,从而达到跳出循环的目的。