本帖最后由 社会佩奇 于 2019-5-27 16:13 编辑
分享一道面试题:
实现一个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
以此类推。
可能的考察点:
1. 链式编程
2. JS流程控制
3. JS事件循环机制
...
思路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")
|