[番外] 手写一个LazyMan

1,098 阅读2分钟

前言

之前看到一个有意思的题目,由于太懒,一直没有整理。最近不想玩游戏,抽空整理一下,小白自己瞎整活儿,有什么说的不对的,还请大家多多指导。

题目是酱紫的:

lazyMan.eat('Breakfest').sleep(4).eat('Launch').sleep(5).eat('Dinner')
代码执行后输出
my name is pig
I ate Breakfest
等待 4 s
I ate Launch
等待 5 s
I ate Dinner

第一步 分析功能

  • 首先,layman 这家伙有 eat sleep 两个基本方法,并且这两个方法都是支持链式调用的。
  • 其次, lazyMansleep 方法具有延时效果

第二步 整理思路

  • 首先 lazyMansleep eat这些方法,基于面向对象的思想,我们可以通过一个类去实现它。
  • 其次,这个家伙每次只能做一件事情,我们就用一个队列去接收他要做的事情,至于这个队列,我们可以放在数组中。
  • 然后,sleep 具有延时效果,我们要使用延时器

第三步 基础代码

class LazyMan {
  constructor(name) {
    // 虽然 lazyMan 很懒,但我们依然给他一个名字
    this.name = name 
    // 我们把 lazyMan 要做的事情放在一个数组里
    this.queue = []
  }
  /**
   * eat
   * @param { String } what
   * @return {*} 
   * @memberof LazyMan
   */
  eat(what) {
    // 返回 this 实现链式调用
    return this
  }
  /**
   * sleep
   * @param { Number } hours
   * @return {*} 
   * @memberof LazyMan
   */
  sleep(hours) {
    // 同样返回 this 实现链式调用
    return this
  }
}

这样一个基本的方法和属性就都有了,接下来就要考虑怎么去实现他的队列方法了。想了很久还是没有想到,没出息的我就去看别人的代码了, >_< 。(没办法,能力有限啊,靠自己不能做到,就要借巨人的肩膀用一下了)然后,真香!好了,看来很多大神的讲解,接下来我们用自己的话复述一下:

既然 eat sleep 都是行为,我们就用函数给他包裹起来,并把他们放到队列里。然后在封装一个 next 方法,从队列里面,把行为拿出来,执行一下。并且在函数执行完成后再调用一下这个next 方法,就能完成队列里的所有行为了,(好吧,其实就是递归执行)ok !开干!

第四步 功能实现

 ...
 /**
   * next
   * @memberof LazyMan
   */
  // 先封装一个 next 方法
  next() {
    // 把要执行的行为抽出来,并且执行一下
    const fn = this.queue.shift()
    fn && fn()
  }
  ...

接下来,就是把行为一个个放进去了

  ...
  /**
   * eat
   * @param { String } what
   * @return {*} 
   * @memberof LazyMan
   */
  eat(what) {
    // eat 是一个行为 我们用函数把它包裹起来
    let fn = (() => {
      // 用一个闭包把吃了什么记录下来
      return () => {
        console.log(`I ate ${what} !`)
        // 行为完成,调用 next 方法,执行下一个行为
        this.next()
      }
    })(what)
    this.queue.push(fn)
    // 返回 this 实现链式调用
    return this
  }
  /**
   * sleep
   * @param { Number } hours
   * @return {*} 
   * @memberof LazyMan
   */
  sleep(hours) {
    const _this = this
    let fn = (function () {
      return function () {
        setTimeout(function () {
          console.log(`I slept ${hours} hours ... ...`)
          // 同样地,行为完成,调用 next 方法,执行下一个行为
          _this.next()
        }, hours * 1000)
      }
    })(hours)
    // 同样地,把 sleep 行为放到队列里
    this.queue.push(fn)
    // 同样返回 this 实现链式调用
    return this
  }
  ...

然后,就是这个队列要怎么执行的问题了

  ...  
  constructor(name) {
   ...
    // 第一个行为
    const fn = (() => {
      return () => {
        console.log(`My name is ${name}`)
        // 开始执行第二个行为
        this.next()
      }
    })()
    this.queue.push(fn)
    // 等实例化完成开始执行队列任务
    setTimeout(() => {
      this.next()
    }, 0)
  }
  ...

执行一下

const lazyman = new LazyMan('Pikky').eat('Breakfest')
lazyman.sleep(4)
lazyman.eat('Launch')
lazyman.sleep(5)
lazyman.eat('Dinner')
lazyman.sleep(8)

// My name is Pikky
// I ate Breakfest !
// I slept 4 hours ... ...
// I ate Launch !
// I slept 5 hours ... ...
// I ate Dinner !
// I slept 8 hours ... ...
// 完美👍👍👍

总结

其实这道题考的是一个 ① 队列的实现,以及 ② 链式调用的实现。开发的过程中,我们也用到了 ③ 事件轮询的机制和 ④ 闭包以及 ⑤ 递归、⑥ 面向对象的相关知识。看似简单的一道题,其实考查的点很多,题目还这么精炼有意思,好题!学海无涯,书山有路。每天一小步,总是在进步。耶!

最后,贴一下完整代码

class LazyMan {
  constructor(name) {
    // 虽然 lazyMan 很懒,但我们依然给他一个名字
    this.name = name
    // 我们把 lazyMan 要做的事情放在一个数组里
    this.queue = []
    // 第一个行为
    const fn = (() => {
      return () => {
        console.log(`My name is ${name}`)
        // 开始执行第二个行为
        this.next()
      }
    })()
    this.queue.push(fn)
    // 等实例化完成开始执行队列任务
    setTimeout(() => {
      this.next()
    }, 0)
  }
  /**
   * next
   * @memberof LazyMan
   */
  next() {
    // 把要执行的行为抽出来,并且执行一下
    const fn = this.queue.shift()
    fn && fn()
  }
  /**
   * eat
   * @param { String } what
   * @return {*} 
   * @memberof LazyMan
   */
  eat(what) {
    // eat 是一个行为 我们用函数把它包裹起来
    let fn = (() => {
      // 用一个闭包把吃了什么记录下来
      return () => {
        console.log(`I ate ${what} !`)
        // 行为完成,调用 next 方法,执行下一个行为
        this.next()
      }
    })(what)
    this.queue.push(fn)
    // 返回 this 实现链式调用
    return this
  }
  /**
   * sleep
   * @param { Number } hours
   * @return {*} 
   * @memberof LazyMan
   */
  sleep(hours) {
    const _this = this
    let fn = (function () {
      return function () {
        setTimeout(function () {
          console.log(`I slept ${hours} hours ... ...`)
          // 同样地,行为完成,调用 next 方法,执行下一个行为
          _this.next()
        }, hours * 1000)
      }
    })(hours)
    // 同样地,把 sleep 行为放到队列里
    this.queue.push(fn)
    // 同样返回 this 实现链式调用
    return this
  }
}