A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 社会佩奇 中级黑马   /  2019-5-27 16:08  /  870 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 社会佩奇 于 2019-5-27 17:55 编辑

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










0 个回复

您需要登录后才可以回帖 登录 | 加入黑马