首页技术文章正文

-LazyMan问题【黑马web前端】

更新时间:2019年07月26日 11时15分26秒 来源:黑马程序员论坛



分享一道面试题:
实现一个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


以此类推。


涉及到的技术点:
链式编程
JS流程控制
JS事件循环机制Promise
。。。

方法1:
问题的关键是如何实现任务的顺序执行。
参考node.js,在Express有一个类似的东西叫中间件,这个中间件和我们这里的吃饭、睡觉等任务很类似。
每一个中间件执行完成后会调用next()函数,这个函数用来调用下一个中间件。
对于这个问题,我们也可以利用相似的思路来解决。
首先创建一个任务队列,然后利用next()函数来控制任务的顺序执行:
[JavaScript] 纯文本查看 复制代码
        function LazyMan(name) {
            // 创建一个任务队列
            let taskList = [];
            // 创建lazyman对象
            let _lazyMan = {
                // 执行下一步 的方法
                next() {
                    // 抽取任务队列中的第一个任务
                    let task = taskList.shift();
                    // 如果存在该任务,就调用该任务
                    task && task()
                },
                // sayHi(打招呼) 方法
                sayHi(name) {
                    // 在任务队列 最后面 追加任务
                    taskList.push(() => {
                        // 打招呼
                        console.log(`Hi! This is ${name}!`);
                        // 该任务完成之后,调用下一步的方法
                        this.next()
                    })
                    // return this 为了实现链式编程
                    return this
                },
                // sleep(睡觉) 方法
                sleep(time) {
                    // 在任务队列 最后面 追加任务
                    taskList.push(() => {
                        // 开启定时器
                        setTimeout(() => {
                            // 输入 多少秒之后醒来
                            console.log(`Wake up after ${time}`);
                            // 该任务完成之后,调用下一步的方法
                            this.next()
                        }, time * 1000);
                    })
                    // return this 为了实现链式编程
                    return this
                },
                eat(food) {
                    taskList.push(() => {
                        console.log(`Eat ${food}~`);
                        this.next()
                    })
                    return this
                },
                // sleepFirst(先睡) 方法
                sleepFirst(time) {
                    // 在任务队列 最前面 添加任务
                    taskList.unshift(() => {
                        // 开启定时器
                        setTimeout(() => {
                            console.log(`Wake up after ${time}`);
                            // 该任务完成之后,调用下一步的方法
                            this.next()
                        }, time * 1000);
                    })
                    // return this 为了实现链式编程
                    return this
                }
            }

            // 手动调用 sayHi方法
            _lazyMan.sayHi(name)
            // 使用定时器,让任务队列在同步线程完成之后再执行
            setTimeout(() => {
                _lazyMan.next()
            }, 0);
            // 暴露 lazyman对象
            return _lazyMan
        }

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

方法2:
   1. 看题目输出示例,可以确定这是拟人化的输出,也就是说:应该编写一个类来定义一类人,叫做LazyMan。可以输出名字、吃饭、睡觉等行为。
 2. 从输出的句子可以看出,sleepFrist的优先级是最高的,其他行为的优先级一致。
 3. 从三个例子来看,都得先调用LazyMan来初始化一个人,才能继续后续行为,所以LazyMan是一个接口。
 4. 句子是按调用方法的次序进行顺序执行的,是一个队列。

[JavaScript] 纯文本查看 复制代码
        // 采用模块模式来编写代码
        (function (window, undefined) {
            // 创建一个任务队列
            var taskList = [];
            // {
            //     'msg': 'LazyMan', // 消息名
            //     'args': 'Hank'    // 参数列表
            // }
            // 订阅
            function subscribe() {
                var args = Array.prototype.slice.call(arguments);

                if (args.length < 1) {
                    throw new Error("subscribe 参数不能为空!");
                }
                // 创建任务
                var task = {
                    msg: args[0], // 消息名
                    args: args.slice(1) // 参数列表
                }

                // 除非是 sleepFirst 向前添加,否则向后追加
                if (task.msg == "sleepFirst") {
                    taskList.unshift(task);
                } else {
                    taskList.push(task);
                }
            }

            // 发布
            function publish() {
                if (taskList.length > 0) {
                    // 调用 run(执行)方法
                    run(taskList.shift());
                }
            }

            // 类
            function LazyMan() {};

            LazyMan.prototype.eat = function (str) {
                subscribe("eat", str);
                return this;
            };

            LazyMan.prototype.sleep = function (num) {
                subscribe("sleep", num);
                return this;
            };

            LazyMan.prototype.sleepFirst = function (num) {
                subscribe("sleepFirst", num);
                return this;
            };

            // 输出文字
            function lazyManLog(str) {
                console.log(str);
            }

            // 具体方法
            // 打招呼
            function lazyMan(str) {
                lazyManLog("Hi!This is " + str + "!");
                publish();
            }
            // 吃
            function eat(str) {
                lazyManLog("Eat " + str + "~");
                publish();
            }
            // 睡
            function sleep(num) {
                setTimeout(function () {
                    lazyManLog("Wake up after " + num);

                    publish();
                }, num * 1000);

            }
            // 先睡
            function sleepFirst(num) {
                setTimeout(function () {
                    lazyManLog("Wake up after " + num);

                    publish();
                }, num * 1000);
            }

            // run(执行)方法:
            function run(option) {
                var msg = option.msg,
                    args = option.args;

                switch (msg) {
                    case "lazyMan":
                        lazyMan.apply(null, args);
                        break;
                    case "eat":
                        eat.apply(null, args);
                        break;
                    case "sleep":
                        sleep.apply(null, args);
                        break;
                    case "sleepFirst":
                        sleepFirst.apply(null, args);
                        break;
                    default:
                        ;
                }
            }

            // 暴露接口
            window.LazyMan = function (str) {
                subscribe("lazyMan", str);

                setTimeout(function () {
                    publish();
                }, 0);

                return new LazyMan();
            };
        })(window);


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


Promise版:
[JavaScript] 纯文本查看 复制代码
function _LazyMan(name) {

    this.promiseGetters = [];

    var makePromise = function  () {
        var promiseObj = new Promise(function(resolve, reject){
            console.log("Hi! This is " + name + "!");

            resolve();
        })

        return promiseObj;
    }

    this.promiseGetters.push(makePromise);

    // 在各个Promise的then函数中,将任务序列穿起来
    var self = this;
    var sequence = Promise.resolve();
    // Promise.resolve 等价于
    // var sequence = new Promise(function (resolve, reject) {
    //     resolve();
    // })
    setTimeout(function(){
        for (var i = 0; i < self.promiseGetters.length; i++) {
            var nowPromiseGetter = self.promiseGetters[i];
            var thenFunc = (function (nowPromiseGetter) {
                return function  () {
                    return nowPromiseGetter()
                }
            })(nowPromiseGetter);

            sequence = sequence.then(thenFunc);
        };

    }, 0); // 在下一个事件循环启动任务
}

_LazyMan.prototype.eat = function(name) {
    var makePromise = function  () {
        var promiseObj = new Promise(function(resolve, reject){
            console.log("Eat " + name + "~");

            resolve();
        })

        return promiseObj;
    }

    this.promiseGetters.push(makePromise);

    return this; // 实现链式调用
}

_LazyMan.prototype.sleep = function(time) {
    var makePromise = function  () {
        var promiseObj = new Promise(function(resolve, reject){

            setTimeout(function(){

                console.log("Wake up after " + time + "s!");

                resolve();

            }, time * 1000);
        })

        return promiseObj;
    }

    this.promiseGetters.push(makePromise);

    return this;
}

/* 封装 */

function LazyMan(name){

    return new _LazyMan(name);

}

LazyMan("Hank").sleep(1).eat("dinner")










推荐了解热门学科

java培训 Python人工智能 Web前端培训 PHP培训
区块链培训 影视制作培训 C++培训 产品经理培训
UI设计培训 新媒体培训 产品经理培训 Linux运维
大数据培训 智能机器人软件开发




传智播客是一家致力于培养高素质软件开发人才的科技公司“黑马程序员”是传智播客旗下高端IT教育品牌。自“黑马程序员”成立以来,教学研发团队一直致力于打造精品课程资源,不断在产、学、研3个层面创新自己的执教理念与教学方针,并集中“黑马程序员”的优势力量,针对性地出版了计算机系列教材50多册,制作教学视频数+套,发表各类技术文章数百篇。

传智播客从未停止思考

传智播客副总裁毕向东在2019IT培训行业变革大会提到,“传智播客意识到企业的用人需求已经从初级程序员升级到中高级程序员,具备多领域、多行业项目经验的人才成为企业用人的首选。”

中级程序员和初级程序员的差别在哪里?
项目经验。毕向东表示,“中级程序员和初级程序员最大的差别在于中级程序员比初级程序员多了三四年的工作经验,从而多出了更多的项目经验。“为此,传智播客研究院引进曾在知名IT企业如阿里、IBM就职的高级技术专家,集中研发面向中高级程序员的课程,用以满足企业用人需求,尽快补全IT行业所需的人才缺口。

何为中高级程序员课程?

传智播客进行了定义。中高级程序员课程,是在当前主流的初级程序员课程的基础上,增加多领域多行业的含金量项目,从技术的广度和深度上进行拓展“我们希望用5年的时间,打造上百个高含金量的项目,覆盖主流的32个行业。”传智播客课程研发总监于洋表示。




黑马程序员热门视频教程【点击播放】

Python入门教程完整版(懂中文就能学会) 零起点打开Java世界的大门
C++| 匠心之作 从0到1入门学编程 PHP|零基础入门开发者编程核心技术
Web前端入门教程_Web前端html+css+JavaScript 软件测试入门到精通


在线咨询 我要报名
和我们在线交谈!