《JS链式调用》

1,534 阅读3分钟

题目:如何实现一个LazyMan?

实现一个LazyMan,可以按照以下方式调用:
LazyMan(“Hank”)输出:
Hi! This is Hank!

LazyMan(“Hank”).sleep(10).eat(“dinner”)输出
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~

LazyMan(“Hank”).eat(“dinner”).eat(“supper”)输出
Hi This is Hank!
Eat dinner~
Eat supper~

LazyMan(“Hank”).sleepFirst(5).eat(“supper”)输出
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper

以此类推。

可以借鉴jQuery的实现方式。要想实现链式调用,可以声明一个函数,这个函数返回一个对象,这个对象里就拥有这些方法,而且每个方法都要返回此对象,才能实现链式调用。也可以声明一个构造函数,new 这个构造函数 会生成一个实例,这些方法就挂在实例的原型上,而且这个方法也都返回当前实例。

还有一个关键点就是考察JS的流程控制。如何才能实现任务的顺序执行。而且遇到sleep还要休息几秒钟,之后再执行它后边的函数。通过使用任务队列next函数来实现。

next函数里是为了执行下一个任务的。具体:从任务队列里取出最前边的那个函数,执行它。

每次 .eat() 之类的,这个eat方法里就定义一个函数,函数里先做它自己做的事,做完以后再去调用next。定义完以后,把这个函数push进任务队列,等待被调用。然后返回当前对象。

这些方法都是按照这种套路来写。先定义一个函数fn,fn里边先做自己的事,然后next()将棒交给下一个任务。然后把fn push进任务队列。最后一定要返回对象,才能链式调用

写的时候要注意this的指向,正确使用普通函数和箭头函数。

  1. 下边是用构造函数的方法:

构造函数要用new来调用。但是这个题目里是LazyMan(“Hank”)来开始,没有new。所以我们可以让LazyMan函数返回new _LazyMan()这个实例,eat这些方法里也返回这个实例。

function _LazyMan(name){
  this.task=[]
  const fn=()=>{
    console.log(`Hi! This is ${name}!`)
    this.next()
  }
  this.task.push(fn)
  setTimeout(()=>{
    this.next()
  },0)
}

_LazyMan.prototype.next=function(){
  let f=this.task.shift()
  f && f()
}

_LazyMan.prototype.eat=function(name){
  const fn=()=>{
    console.log(`Eat ${name}~`)
    this.next()
  }  //eat里的this是对象实例,为了让fn里的this也是对象实例,fn要用箭头函数
  this.task.push(fn)
  return this
}

_LazyMan.prototype.sleep=function(time){
  const fn=()=>{
    setTimeout(()=>{
      console.log(`Wake up after ${time}`)
      this.next()
    },time*1000)
  }
  this.task.push(fn)
  return this
}

_LazyMan.prototype.sleepFirst=function(time){
  const fn=()=>{
    setTimeout(()=>{
      console.log(`Wake up after ${time}`)
      this.next()
    },time*1000)
  }
  this.task.unshift(fn)  //先睡,再干别的。可以把fn放在队列最开头,而不是放末尾
  return this
}

function LazyMan(name){
  return new _LazyMan(name)
}

LazyMan('Hank').eat('dinner').eat('supper')
//LazyMan('Hank').sleep(3).eat('dinner')
//LazyMan('Hank').sleepFirst(5).eat('supper')

  1. 另一个题目,用普通函数实现。返回的对象是函数里定义的一个普通对象。

实现一个chain, eat函数打印eat,work函数打印work,sleep函数休息

chain().eat().sleep(5).work().eat().work().sleep(10)

function chain(){
  let tasks=[]
  const fn=()=>{
    obj.next()
  }
  tasks.push(fn)
  setTimeout(()=>{
    obj.next()
  },0)
  let obj= {
    next(){
      const f=tasks.shift()
      f && f()
    },
    eat(){
      const fn=()=>{
        console.log('eat')
        this.next()
      }  //eat里的this是obj,因为obj.eat()。要想让fn里的this也是obj,fn就要是箭头函数
      tasks.push(fn)
      return obj
    },
    work(){
      const fn=()=>{
        console.log('work')
        this.next()
      }
      tasks.push(fn)
      return obj
    },
    sleep(time){
      const fn=()=>{
        setTimeout(()=>{
          this.next()
        },time*1000)
      }
      tasks.push(fn)
      return obj
    }
  }
  return obj
  
}