链式调用,发布订阅模式,柯里化,sleep函数手写代码题

1,465 阅读3分钟

实现链式调用

重点是return this 和任务队列顺序

class Task {
    constructor(){
        this.list=[];
        this.logNum=0;
        this.waitNum=0;
        setTimeout(()=>{
            this.next();   
        })
    }
    log(n){
        this.logNum=n;
        const fn=()=>{
            let num=n
            console.log(num)
            this.next()
        }
        this.list.push(fn)
        return this;
    }
    wait(n){
        this.waitNum=n
        const fn=()=>{
            setTimeout(()=>{
                console.log(`等待了${n}秒`)
                this.next()
            },n*1000)
        }
        this.list.push(fn)
        return this;
    }
    next(){
        const fn=this.list.shift()
        fn&&fn();
    }
}
const t=new Task()
// console.log(t)
t.log(1).log(2).wait(3).log(4).wait(2).wait(3).log(6)

使用promise实现:

class U {
  constructor() {
    this.promise = Promise.resolve();
  }

  console(val) {
    this.promise = this.promise.then(() => {
      console.log(val);
    });
    return this;
  }

  setTimeout(wait) {
    this.promise = this.promise.then(() => {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve();
        }, wait);
      });
    });
    return this;
  }
}
const u = new U();
u.console("breakfast")
  .setTimeout(3000)
  .console("lunch")
  .setTimeout(3000)
  .console("dinner");

实现发布订阅模式

class EventEmitter {
    constructor() {
        //存储事件
        this.cache = {}
    }
    //订阅事件的方法on(eventName, callback)
    on(name, fn) {
        if (this.cache[name]) {
            this.cache[name].push(fn)
        } else {
            this.cache[name] = [fn]
        }
    }
    //移除事件
    off(name, fn) {
        let tasks = this.cache[name]
        if (tasks) {
            const index = tasks.findIndex(f => f === fn || f.callback === fn)
            if (index >= 0) {
                tasks.splice(index, 1)
            }
        }
    }
    //触发事件的方法emit(eventName, callback)
    emit(name, once = false, ...args) {
        if (this.cache[name]) {
            // 创建副本(一层深拷贝),如果回调函数内继续注册相同事件,会造成死循环
            let tasks = this.cache[name].slice()
            for (let fn of tasks) {
                fn(...args)
            }
            //是否只执行一次
            if (once) {
                delete this.cache[name]
            }
        }
    }
}
//   简单使用
let newEvent = new EventEmitter()
newEvent.on('event1', function(arg) {
    console.log('事件1', arg)
})
newEvent.on('event2', function(arg) {
    console.log('事件2', arg)
})
newEvent.emit('event1',false, {name: 'anyway'})
newEvent.emit('event2', false ,{age: '28'})
//事件1 { name: 'anyway' }
//事件2 { age: '28' }

柯里化函数的实现

柯里化函数的定义:将多参数的函数转换成单参数的形式

柯里化函数实现的原理:利用闭包原理在执行可以形成一个不销毁的作用域,然后把需要预先处理的内容都储存在这个不销毁的作用域中,并且返回一个最少参数函数。

第一种:固定传入参数,参数够了才执行

/**
 * 实现要点:柯里化函数接收到足够参数后,就会执行原函数,那么我们如何去确定何时达到足够的参数呢?
 * 柯里化函数需要记住你已经给过他的参数,如果没给的话,则默认为一个空数组。
 * 接下来每次调用的时候,需要检查参数是否给够,如果够了,则执行fn,没有的话则返回一个新的 curry 函数,将现有的参数塞给他。
 * 
 */
// 待柯里化处理的函数
let sum = (a, b, c, d) => {
  return a + b + c + d
}

let curry = (fn, ...args) => {
  return (...newArgs)=>{
      let resArgs=args.concat(newArgs);
      //fn.length 返回函数的形参个数
      if(fn.length===resArgs.length){
          return fn(...args,...resArgs)
      }else{
          return curry(fn,...resArgs)
      }
  }
};

var sumPlus = curry(sum)
sumPlus(1)(2)(3)(4)
sumPlus(1, 2)(3)(4)
sumPlus(1, 2, 3)(4)

第二种:不固定传入参数,随时执行

/**
 * 当然了,柯里化函数的主要作用还是延迟执行,执行的触发条件不一定是参数个数相等,也可以是其他的条件。
 * 例如参数个为0的情况,那么我们需要对上面curry函数稍微做修改
 */
// 待柯里化处理的函数
let sum = arr => {
  return arr.reduce((a, b) => {
    return a + b
  })
}

let curry = (fn, ...arr) => {  // arr 记录已有参数
  return (...args) => {  // args 接收新参数
    if (args.length === 0) {  // 参数为空时,触发执行
      return fn(...arr, ...args)
    } else {  // 继续添加参数
      return curry(fn, ...arr, ...args)
    }
  }
}

var sumPlus = curry(sum)
sumPlus(1)(2)(3)(4)()
sumPlus(1, 2)(3)(4)()
sumPlus(1, 2, 3)(4)()

add(1)(2)(3)=6

function add(x) {
  var sum = x;
  var res = function (x) {
    sum = sum + x;
    return res;
  };
  // 此处是在函数上挂载一个方法,    
  res.out = function () {
    return sum;
  };
  return res;
}

sleep函数

第一种方式:使用Promise + setTimeout实现
function sleep(time) {
    return new Promise(resolve => {
        console.log(resolve)
        setTimeout(resolve, time)
    })
}

sleep(10000).then(res => {
    console.log(`sleep exe ending!`)
})

第二种方式:使用callback回调

function sleep(time, callback) {
    return new Promise(resolve => setTimeout(callback, time))
}
sleep(3000, () => {
    console.log(`sleep exe ending!`)
})