javascript - 一个面试题,有啥想法么?
伊谢尔伦
伊谢尔伦 2017-04-11 12:56:36
[JavaScript讨论组]

实现函数LazyMan,使得:

LazyMan('Hank'); 输出
Hi Hank!

LazyMan('Hank').eat('dinner');输出
Hi Hank!
Eat dinner!

LazyMan('Hank').sleep(5).eat('dinner'); 输出:
Hi Hank!
//等待五秒
Eat dinner!

LazyMan('Hank').sleepFirst(5).eat('dinner');输出:
//等待五秒
Hi Hank!
Eat dinner!
伊谢尔伦
伊谢尔伦

小伙看你根骨奇佳,潜力无限,来学PHP伐。

全部回复(9)
ringa_lee

2017-3-22 代码再次更新,受一道面试题的启发。重新优化了代码(不等待时,setTimeout的间隔时间为0):

function LazyMan(nick){
    var obj = {
        task : {
            list : [],
            add : function(fun, timer){
                timer = timer || 0;
                this.task.list.push({
                    fun : fun,
                    timer : timer
                });
                return this;
            },
            run : function(){
                if( this.task.list.length > 0 ){
                    setTimeout( (function(){
                        this.task.list.shift().fun.call(this);
                    }).bind(this), this.task.list[0].timer );
                }else{
                    this.echo("[Task]", "==========Stop==========");
                }
            }
        },
        echo : function( str , str2 ){
            console.log( str + ' ' + str2 );
            return this;
        },
        hello : function( nick ){
            return this.task.add.call(this, function(){
                this.echo('Hi', nick);
                this.task.run.call(this);
            });
        },
        eat : function( eat ){
            return this.task.add.call(this, function(){
                this.echo('Eat', eat);
                this.task.run.call(this);
            });
        },
        sleep : function( timer ){
            return this.task.add.call(this, function(){
                this.echo("[Timer( sleep )]", timer);
                this.task.run.call(this);
            }, timer * 1000);
        },
        sleepFirst : function( timer ){
            var fun = this.task.list[0].fun;
            this.task.list[0].fun = function(){
                setTimeout((function(){
                    this.echo("[Timer( sleepFirst) ]", timer);
                    fun.call(this);
                }).bind(this), timer * 1000);
            };
            return this;
        }
    };
    obj.echo("[Task]", "==========Start==========").hello(nick).task.run.call(obj);
    return obj;
};

LazyMan("A").sleepFirst(1).eat("abc").sleep(4).sleep(5).eat("A").eat("B").eat("C")

得到启发的代码如下:

var a = true;
setTimeout(function(){
    a = false;
}, 0);
while( a == true ){}

alert('x'); //alert永远不会执行。JS作为一个单线程语言,只有处理完队列中的任务后,才会去去调用setTimeout的队列。

以下是原答案:

重新来答一遍,这次是认真的,使用了任务队列,同样只使用了一个函数,没用使用Promise,没有使用 timer累加的方法,保证最多只有一个计时器在运行,同时能获取任务开始和最终结束时间。

核心部分是一个 setTimeout 和 一个 任务队列(数组)。

因为使用了队列流程,处理完一个再处理下一个,所以支持多次输出,多次等待,而程序不会乱掉。 LazyMan("A").sleepFirst(1).eat("abc").sleep(4).sleep(5).eat("A").eat("B").eat("C")

function LazyMan(nick){
    var obj = {
        task : {
            list : [],
            add : function(timer, fun){
                timer = timer || 16;
                this.task.list.push({
                    fun : fun,
                    timer : timer
                });
                return this;
            },
            run : function(){
                if( this.task.list.length > 0 ){
                    setTimeout( (function(){
                        this.task.list.shift().fun.call(this);
                    }).bind(this), this.task.list[0].timer );
                }else{
                    this.echo("[Task]", "==========Stop==========");
                }
            }
        },
        echo : function( str , str2 ){
            console.log( str + ' ' + str2 );
            return this;
        },
        hello : function( nick ){
            return this.task.add.call(this, 16, function(){
                this.echo('Hi', nick);
                this.task.run.call(this);
            });
        },
        eat : function( eat ){
            return this.task.add.call(this, 16, function(){
                this.echo('Eat', eat);
                this.task.run.call(this);
            });
        },
        sleep : function( timer ){
            return this.task.add.call(this, timer * 1000, function(){
                this.echo("[Timer( sleep )]", timer);
                this.task.run.call(this);
            });
        },
        sleepFirst : function( timer ){
            var fun = this.task.list[0].fun;
            this.task.list[0].fun = function(){
                setTimeout((function(){
                    this.echo("[Timer( sleepFirst) ]", timer);
                    fun.call(this);
                }).bind(this), timer * 1000);
            };
            return this;
        }
    };
    obj.echo("[Task]", "==========Start==========").hello(nick).task.run.call(obj);
    return obj;
};

测试下结果:

LazyMan('Hank');
/*
[Task] ==========Start==========
Hi Hank
[Task] ==========Stop==========
*/

LazyMan('Hank').eat('dinner');
/*
[Task] ==========Start==========
Hi Hank
Eat dinner
[Task] ==========Stop==========
*/

LazyMan('Hank').sleep(5).eat('dinner');
/*
[Task] ==========Start==========
Hi Hank
[Timer( sleep )] 5
Eat dinner
[Task] ==========Stop==========
*/

LazyMan('Hank').sleepFirst(5).eat('dinner');
/*
[Task] ==========Start==========
[Timer( sleepFirst) ] 5
Hi Hank
Eat dinner
[Task] ==========Stop==========
*/

LazyMan('来搞笑的小吃货').sleepFirst(1).eat('西瓜').eat('甜瓜').eat('哈密瓜').sleep(2).eat('苹果').eat('橘子').eat('香蕉').sleep(3).sleep(4).eat('撑死了');
/*
[Task] ==========Start==========
[Timer( sleepFirst) ] 1
Hi 来搞笑的小吃货
Eat 西瓜
Eat 甜瓜
Eat 哈密瓜
[Timer( sleep )] 2
Eat 苹果
Eat 橘子
Eat 香蕉
[Timer( sleep )] 3
[Timer( sleep )] 4
Eat 撑死了
[Task] ==========Stop==========
*/

正规作答结束,以下是原答案:


民科解法,欢迎来喷。强迫症,没有使用 任务队列,和 @xiaoboost 答案不同的是,不支持 LazyMan('Hank').sleepFirst(2).sleep(3).eat('lunch').sleep(4).eat('dinner'); 这种方式调用。

不在乎中间过程,只在乎结果。

稍后用队列的方法,重写一遍。

function LazyMan(nick){
    var obj = {
        data : {},
        echo : function( str , str2 ){
            console.log( str + ' ' + str2 );
        },
        hello : function( nick ){
            this.data.nick = nick || this.data.nick;
            setTimeout( (function(){
                if( ! this.data.waiting ){
                    this.echo('Hi', this.data.nick);
                }
            }).bind(this), 16);
            return this;
        },
        eat : function( eat ){
            this.data.eat = eat || this.data.eat;
            setTimeout( (function(){
                if( ! this.data.sleep ){
                    this.echo('Eat', this.data.eat);
                }
            }).bind(this), 16);
            return this;
        },
        sleep : function( timer ){
            this.data.sleep = true;
            setTimeout( (function(){
                this.data.sleep = false;
                this.eat();
            }).bind(this), timer * 1000);
            return this;
        },
        sleepFirst : function( timer ){
            this.data.waiting = true;
            this.data.sleep = true;
            setTimeout( (function(){
                this.data.waiting = false;
                this.hello();
                this.data.sleep = false;
                this.eat();
            }).bind(this), timer * 1000);
            return this;
        }
    };
    return obj.hello(nick);
}
高洛峰

这道题有点意思,要完成这个任务有点麻烦,我算是稍微取了点巧吧。

我个人是使用了异步来实现的。
Man类内部维护了一个任务队列,所有的类方法都会直接操作任务队列的顺序。
由于类生成时候设定的异步函数必须要等主进程完成之后才会启动,所以它是不会影响到主进程时候的任务队列操作的。
主进程运行结束,类定义时候的setTimeout进程启动,它会把队列中的所有方法用then串联起来,之后任务队列就会按照顺序依次运行了。

当然这还是能随意组合的,比如这样:

LazyMan('Hank').sleepFirst(2).sleep(3).eat('lunch').sleep(4).eat('dinner');
// 等待2秒
// Hi Hank
// 等待3秒
// eat lunch
// 等待4秒
// eat dinner

代码在这里:

function Man(str) {
    // 默认函数
    function start(resolve) {
        console.log(`Hi ${str}`);
        resolve();
    }
    // 初始化队列
    this.queue = [start];
    
    setTimeout(() => {
        let start = Promise.resolve();
        for (let i = 0; i < this.queue.length; i++) {
            start = start.then(() => new Promise(this.queue[i]));
        }
    })
}
Man.prototype.eat = function(food) {
    function eatFood(resolve) {
        console.log(`Eat ${food}`);
        resolve();
    }
    this.queue.push(eatFood);
    return this;
};
Man.prototype.sleep = function(time) {
    function sleep(resolve) {
        setTimeout(resolve, time * 1000);
    }
    this.queue.push(sleep);
    return this;
};
Man.prototype.sleepFirst = function(time) {
    function sleep(resolve) {
        setTimeout(resolve, time * 1000);
    }
    this.queue.splice(0, 0, sleep);
    return this;
};

function LazyMan(...args) {
    return new Man(...args);
}
怪我咯

赶项目进度中换换脑子,随手试写。可以添加新方法,可以任意组合。感觉写得一般。

LazyMan('Hank').sleepFirst(5).sleep(5).eat('breakfast').sleep(5).eat('lunch').sleep(5).eat('dinner');
function LazyMan(name) {
  const lazyMan = {
    name,
    timer: 0,
    firstTimer: 0,
    init: false,
    
    handle: function(cb, prop) {
      const _this = this;
      
      if(!this.init) {
        this.init = true;
        if(this.firstTimer > 0) {
          setTimeout(function() {
            _this.say(_this.name);
          }, _this.firstTimer * 1000);
        } else {
          _this.say(_this.name);
        }
      }
      
      setTimeout(function() {
        cb.call(_this, prop);
      }, this.timer * 1000);
    },

    eat: function(food) {
      this.handle(function(food) {
        console.log(`Eat ${food}`);
      }, food);
      return this;
    },

    sleep: function(timer) {
      this.timer += timer;
      return this;
    },

    sleepFirst: function(timer) {
      this.timer += timer;
      this.firstTimer += timer;
      return this;
    },

    say: function(name) {
      console.log(`Hi ${name}`);
      return this;
    }
  }
  return lazyMan;
}
黄舟

哈哈 自己的答案

怪我咯

我也来写一个

var lazymen=(function(){
        var arr=[];
        var i=0;
        function next(){
            console.log(arr)
            if(arr[i]){
                arr[i++]();
            }else{i=0;arr=[]}
            
        }
        var cb=function(fn){
            var arg=[].slice.call(arguments,1)
            return function(){
                fn.apply(null,arg)
            }
        }
        var say=function(x,y){
            console.log(x+' '+y);
            next()
        }
        var lazymen=function(str){
            arr.push(cb(say,'Hi',str))
            setTimeout(function(){
                next()
            },0)
        }
        var  sleep=function(num){
                setTimeout(function(){next()},num*1000)
        }
        lazymen.prototype.eat=function(str){
            arr.push(cb(say,'eat',str))
            return this
        };
        lazymen.prototype.sleep=function(num){
            arr.push(cb(sleep,num))
            return this
        }
        lazymen.prototype.sleepFirst=function(num){
            arr.unshift(cb(sleep,num))
            return this
        }
        return lazymen
    })()
    var LazyMan=function(str){
        return new lazymen(str)
    }
PHP中文网

前些天看过这个问题,https://www.madcoder.cn/inter...,我把这个大牛写的答案复制到这里。


class C {
    constructor (name) {
        this.tasks = [];
        setTimeout(() => this.next());
        return this.push(() => new Promise(r => console.log(`Hi! This is ${name}`) || r()));
    }
    next () {
        let task = this.tasks.shift();
        task && task().then(() => this.next());
    }
    push (v) {
        this.tasks.push(v);
        return this;
    }
    unshift (v) {
        this.tasks.unshift(v);
        return this;
    }
    sleep (sec) {
        return this.push(() => new Promise(r => console.log(`//等待${sec}秒..`) || setTimeout(() => console.log(`Wake up after ${sec}`) || r(), 1000 * sec)));
    }
    sleepFirst (sec) {
        return this.unshift(() => new Promise(r => console.log(`//等待${sec}秒..`) || setTimeout(() => console.log(`Wake up after ${sec}`) || r(), 1000 * sec)));
    }
    eat (name) {
        return this.push(() => new Promise(r => console.log(`Eat ${name}`) || r()));
    }
}
const LazyMan = function (name) {
    return new C(name);
}
LazyMan('Gcaufy').eat('dinner').sleep(3).eat('supper').sleepFirst(2);
黄舟
const delay = d => new Promise(resolve => setTimeout(resolve, d))

class LM {
  constructor(name) { 
    this.name = name 
    this.queue = Promise.resolve()
        .then(_ => this.first)
        .then(_ => console.log(`Hi ${this.name}`))
  }
  
  eat(food) {
    this.queue = this.queue.then(_ => console.log(`Eat ${food}`))
    return this
  }
  
  sleep(d) {
    this.queue = this.queue.then(_ => delay(d * 1000))
    return this
  }
  
  sleepFirst(d) {
    this.first = delay(d * 1000)
    return this
  }
}

function LazyMan(name) {
  return new LM(name)
}
高洛峰
const LazyMan = function (name) {
  let queue = []
  const cb = function () {
    const fn = queue.shift()
    fn && fn()
  }
  setTimeout(function () {
    cb()
  })
  queue.push(function () {
    console.log(`hi ${name}`)
    cb()
  })
  return {
    sleep: function (time) {
      queue.push(function () {
        setTimeout(cb, time * 1000)
      })
      return this
    },
    sleepFirst: function (time) {
      queue.splice(0, 0, function () {
        setTimeout(cb, time * 1000)
      })
      return this
    },
    eat: function (food) {
      queue.push(function () {
        console.log(`eat ${food}`)
        cb()
      })
      return this
    }
  }
}
LazyMan('alex').sleep(1).eat('
阿神

有点 Promise 的味道,参考了 pinkie 的实现

/**
 * https://segmentfault.com/q/1010000008745355
 * 
 * @author Ivan Yan
 */

function Callbacks() {
  this.queue = []
  this.timer = false
}

Callbacks.prototype.push = function (callback, wait) {
  this.queue.push([callback, wait])

  if (!this.timer) {
    this.timer = true
    var next = typeof setImmediate === 'undefined' ? setTimeout : setImmediate
    next(this.flush.bind(this), 0)
  }
}

Callbacks.prototype.delayAll = function (callback, wait) {
  this.queue.unshift([callback, wait])
}

Callbacks.prototype.flush = function () {
  while (this.queue.length) {
    var item = this.queue.shift()
    var wait = item[1]
    if (wait) {
      setTimeout(this.flush.bind(this), wait)
      return
    }

    item[0]()
  }

  this.queue = []
  this.timer = false
}

function noop() {}

function LazyMan(name) {
  if (!(this instanceof LazyMan)) {
    return new LazyMan(name)
  }
  this.name = name
  this.callbacks = new Callbacks()
  this.hi()
}

LazyMan.prototype._then = function (callback) {
  this.callbacks.push(callback)
  return this
}

LazyMan.prototype.hi = function () {
  var name = this.name
  return this._then(function () {
    console.log('Hi ' + name)
  })
}

LazyMan.prototype.eat = function (sth) {
  return this._then(function () {
    console.log('Eat ' + sth)
  })
}

LazyMan.prototype.sleep = function (s) {
  this.callbacks.push(noop, s * 1000)
  return this
}

// sleepFirst 延迟了前面的回调 hi
// 问题是延迟前面一个,还是延时前面所有的?
// 这里延时所有的回调
LazyMan.prototype.sleepFirst = function (s) {
  this.callbacks.delayAll(noop, s * 1000)
  return this
}

// test
// LazyMan('Hank')
// LazyMan('Hank').eat('dinner')
// LazyMan('Hank').sleep(5).eat('dinner')
// LazyMan('Hank').sleepFirst(5).eat('dinner')
// LazyMan('Hank').sleepFirst(2).eat('dinner').sleepFirst(3) // 同上
LazyMan('Hank').sleepFirst(5).eat('launch').sleep(5).eat('dinner')
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号