40、js实现LazyMan

一、问题描述:

实现一个LazyMan,可以按照以下方式调用:
(1)LazyMan(“Hank”)

输出:
Hi! This is Hank!

(2)LazyMan(“Hank”).sleep(10).eat(“dinner”)

输出:
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner~

(3)LazyMan(“Hank”).eat(“dinner”).eat(“supper”)

输出:
Hi This is Hank!
Eat dinner~
Eat supper~

(4)LazyMan(“Hank”).sleepFirst(5).eat(“supper”)

输出:
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper

以此类推。

二、思路:

关键是输出的顺序,以及需要在每一个任务执行完再执行下一步,类似promise(可以使用promise的方式实现),使用队列也可以。链式调用则要求每个方法都得返回当前对象

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

 注意:

  1. 执行任务不能紧跟在插入任务的后面执行,等所有任务都进队了,才开始执行任务队列。那我们借助setTimeout函数,将他们分进两个事件队列就好了;
  2. 一个任务完成了,我们通过尾调用,通知任务队列去取下一个任务;

三、常规方法实现

var LazyMan = function(name) { //LazyMan方法
    if (!(this instanceof LazyMan)) {
        return new LazyMan(name);
    }
    this.quene = [];
    var self = this;
    var fn = function() { //定义打印名字的方法fn
        console.log('Hi! This is ' + name);
        self.next();
    };
    this.quene.push(fn); //将方法fn入队
    
    //通过settimeout的方法,将执行函数放入下一个事件队列中,从而达到先注册事件,后执行的目的
    setTimeout(function() {
        self.next();
    }, 0);
}
LazyMan.prototype = {//LazyMan的原型
    next: function() { //尾调用函数。一个任务执行完再调用队列中的下一个任务
        if (this.quene.length) {
            var fn = this.quene.shift();
            if ((typeof fn).toLowerCase() === 'function') {
                fn();
            }
        }
    },
    sleep: function(time) {
        var self = this;
        var fn = function() { //睡觉方法
            setTimeout(function() {
                console.log('Wake up after ' + time);
                self.next();
            }, time * 1000);
        };
        this.quene.push(fn); //将睡觉的方法进队
        return this;
    },
    sleepFirst: function(time) {
        var self = this;
        var fn = function() {
            setTimeout(function() {
                console.log('Wake up after ' + time);
                self.next();
            }, time * 1000);
        };
        //sleepFirst函数需要最先执行,所以我们需要在任务队列前面放入,然后再执行后面的任务
        this.quene.unshift(fn);
        return this;
    },
    eat: function(food) {
        var self = this;
        var fn = function() {
            console.log('Eat ' + food + '~');
            self.next();
        };
        this.quene.push(fn);
        return this;
    }
};
//LazyMan("Hank")

//LazyMan("Hank").sleep(10).eat("dinner")

//LazyMan("Hank").eat("dinner").eat("supper")

//LazyMan("Hank").sleepFirst(5).eat("supper")
//LazyMan("Hank").next()

四、使用promise实现

 参考:https://www.jianshu.com/p/9552ca809035

//1._lazyMan构造函数
	function _lazyMan(name){
		this.name = name;
		this.promises = []; //存放所有的promise操作
		var myfunc = () =>{ //返回一个promise
			console.log(`Hi this is ${this.name}!`);
			return Promise.resolve();
		}
		this.promises.push(myfunc); //将打印名字的方法放到promises数组
		var template = Promise.resolve(); //获取一个Promise对象
		setTimeout(()=>{ //箭头函数,使其中的this指向_lazyMan; 不使用将指向window
			this.promises.forEach(v => { //遍历所有的promise,并执行
				template = template.then(v);
			})
		},0)
	}

	//2. lazyMan原型
	_lazyMan.prototype = {

		sleepFirst: function(time){
			var sfpfun = function(){
				return new Promise(function(resolve,reject){ //返回Promise对象
					setTimeout(()=>{
						console.log(`Wake up after ${time}s`);
						resolve();
					},time*1000);
				})
			}
			this.promises.unshift(sfpfun); //放到队头即可
			return this; 
		},
		sleep: function(time){
			var spfun = function(){
				return new Promise(function(resolve,reject){ //返回Promise对象
					setTimeout(()=>{
						console.log(`Wake up after ${time}s`);
						resolve();
					},time*1000);
				});
			}
			this.promises.push(spfun);
			return this; //返回整个对象,就能实现链式调用
		},
		eat: function(food){
			var epfun = function(){ //返回Promise对象。保证遍历数组时,执行then操作
				console.log(`Eat ${food}~`);
				return Promise.resolve();
			}
			this.promises.push(epfun);
			return this;
		}
	}
    //3. 实例化_lazyMan对象
	function lazyMan(name){ 
		return new _lazyMan(name); 
	}
	lazyMan('ch').eat('泡芙').eat('冰淇淋').sleepFirst(4).sleep(2);

输出:

Wake up after 4s
Hi this is ch!
Eat 泡芙~
Eat 冰淇淋~
Wake up after 2s

算法分析: 

1) 箭头函数

箭头函数是ES6 新推出的形式,值得注意的是在setTimeOut的函数中用到了this,如果是传统函数,这个this指向的是window,并不是_LazyMan,但是如果是箭头函数,this就是指向 _LazyMan

2)setTimeOut

setTimeOut函数并不会直接将回调函数放在事件循环队列中,而是等到定时器时间到了之后才会将函数放到事件循环队列中。正是基于setTimeOut函数的这种特性,所以在_LazyMan构造函数中用setTimeout()去获取promises中数组的数据,此时promises的数据是调用链中的所有Promise对象,如果不用setTimeout函数那么在构造函数中,语句顺序执行,promises里的对象只有构造函数中的一个Promise对象,链式调用函数中存入的Promise对象获取不到。

3) 链式调用

return this :返回整个对象,就能实现链式调用。(在原型的eat、sleep、sleepFirst中)

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值