本帖最后由 社会佩奇 于 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")
|