// 实现一个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
// 以此类推。
由问题可以看出要实现 LazyMan 需要注意几个点:
- 顺序问题,一般会按顺序执行,但可以插入任务(sleepFirst)
- 链式调用
解决第一个点,可以选择用队列(数组来模拟)来存起来链式调用的方法,因为数组可以满足:顺序,插入任务这两个条件。但也引出了2个问题:
- 队列存起来的方法,出队时才会执行打印对应的语句的操作,可以理解为,出队的时候才会执行方法的真正操作
- 执行完后,怎样执行下一个方法呢
为了解决问题1,可以选择返回一个可执行函数来解决,一般来说会有参数传入,可以选择IIFE来处理
var fn = (function(args) {
var args = args;
return function() {
// 可以使用args
...
}
})(args)
解决问题2,我们可以造一个方法,用来顺序地让队列中的方法出队且主动调用队列中的方法。
// 只是一个示意函数,tasks是任务队列
function next() {
var fn = tasks.pop();
typeof fn === 'function' && fn();
}
好了,这里已经是解决掉了第一个点,那么第二点,需要链式调用呢,这个比较简单,方法返回this即可。
那现在写一下整体的代码吧。
function LazyMan(name) {
// 判断 this 是否指向 LazyMan
if (!(this instanceof LazyMan)) {
return new LazyMan(name);
}
var self = this;
this.tasks = [];
// 关键1: IIFE返回一个可执行函数
var fn = (function(name){
return function() {
console.log('Hi!This is ' + name);
self.next();
}
})(name);
// 默认状态下,调用LazyMan,会返回 fn
this.tasks.push(fn);
setTimeout(function() {
// 关键2: 开始调用队列的第一个方法
self.next();
}, 0)
}
// 关键3: next 方法
LazyMan.prototype.next = function() {
// 出队
var fn = this.tasks.shift();
typeof fn === 'function' && fn();
};
LazyMan.prototype.eat = function(x) {
let self = this;
var fn = (function(x) {
return function() {
console.log('Eat '+ x);
self.next();
}
})(x);
this.tasks.push(fn);
// 关键4: 链式调用的关键
return this;
};
LazyMan.prototype.sleep = function(x) {
let self = this;
var fn = (function(x) {
return function() {
setTimeout(function() {
console.log('Wake up after '+ x + ' s');
self.next();
}, x * 1000);
}
})(x);
this.tasks.push(fn);
return this;
};
LazyMan.prototype.sleepFirst = function(x) {
let self = this;
var fn = (function(x) {
return function() {
setTimeout(function() {
console.log('Wake up after '+ x + ' s');
self.next();
}, x * 1000);
}
})(x);
// 关键5: 插到队列最前面
this.tasks.unshift(fn);
return this;
};
LazyMan('alili').sleep(2).eat('dinner').sleepFirst(1);
// Wake up after 1 s
// Hi!This is alili
// Wake up after 2 s
// Eat dinner
参考: