LazyMan题目讲解

题目是从知乎上看到的:https://zhuanlan.zhihu.com/p/28892523?utm_medium=social&utm_source=qq

一、题目

实现一个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.整体是一个js的面向对象编程的题目
2.涉及到异步控制的思想
3.执行顺序不同于调用顺序
4.可以考虑内部维护一个数组控制调用顺序
5.可以考虑使用Promise实现
6.可以考虑使用async实现

三、解题代码实现

1.ES5原生代码实现——个人手打调试

    function Man(name) {
        this.actions = [];
        this.current = 0;

        var helloFunc = function () {
            console.log("Hi This is " + name + "!");
            this.act();
        }.bind(this)

        this.addAction(helloFunc);

        setTimeout(function () {
            this.act();
        }.bind(this), 0);
    }

    // 调用下一个动作
    Man.prototype.act = function () {
        var actions = this.actions;
        return actions[this.current] && actions[this.current++]();
    }

    // 向动作队列添加动作
    Man.prototype.addAction = function (func, isFirst) {
        if (!isFirst) {
            this.actions.push(func);
        } else {
            this.actions.unshift(func);
        }
    }

    // 休眠
    Man.prototype.sleep = function (time) {
        var sleepFunc = function () {
            setTimeout(function () {
                console.log("Wake up after " + time + "ms");
                this.act();
            }.bind(this), time)
        }.bind(this);
        this.addAction(sleepFunc);
        return this;
    }

    // 吃饭
    Man.prototype.eat = function (food) {
        var eatFunc = function () {
            console.log("Eat " + food + "~");
            this.act();
        }.bind(this);
        this.addAction(eatFunc);
        return this;
    }

    // 睡觉优先
    Man.prototype.sleepFirst = function (time) {
        var sleepFirstFunc = function () {
            setTimeout(function () {
                console.log("Wake up after " + time + "ms");
                this.act();
            }.bind(this), time)
        }.bind(this);
        this.addAction(sleepFirstFunc, true);
        return this;
    }

    function LazyMan(name) {
        return new Man(name);
    }

重点代码解析

    var helloFunc = function () {
        console.log("Hi This is " + name + "!");
        this.act();
    }.bind(this)
如果不使用.bind(this)那么this指向哪里?答案是 this.actions

为什么会出现这种情况呢?
javascript的this指向执行时的上下文对象
那么this.act()何时执行呢?答案是

    return actions[this.current] && actions[this.current++]();

所以必须使用.bind(this)改变this的指向

简化版可以参考这个代码:

    var a = [0,1,2,function(){console.log(this)}];
    a[3]();

    输出结果为:[0, 1, 2, ƒ]

2.ES6语法实现——ES6对this的控制简直完美,会ES5的实现ES6简直so easy

    class Man{
        constructor(name){
            this.actions = [];
            this.current = 0;

            var helloFunc = () =>{
                console.log("Hi This is " + name + "!");
                this.act();
            }

            this.addAction(helloFunc);

            setTimeout(() =>{
                this.act();
            }, 0)
        }

        act(){
            var actions = this.actions;
            return actions[this.current] && actions[this.current++]();
        }

        addAction(func, isFirst){
            if(!isFirst){
                this.actions.push(func)
            }else{
                this.actions.unshift(func)
            }
        }

        eat(food){
            var eatFunc = () =>{
                console.log("Eat " + food + "~");
                this.act();
            }

            this.addAction(eatFunc)
            return this;
        }

        sleep(time){
            var sleep = () =>{
                setTimeout(() => {
                    console.log("Wake up after " + time + "ms");
                    this.act();
                }, time)
            }

            this.addAction(sleep);
            return this;
        }

        sleepFirst(time){
            var sleepFirst = () =>{
                setTimeout(() =>{
                    console.log("Wake up after " + time + "ms");
                    this.act();
                }, time)
            }

            this.addAction(sleepFirst, true)
            return this;
        }
    }
    function LazyMan(name) {
        return new Man(name)
    }

3.Promise语法实现——知乎网友南塔托给出的答案

对于这个答案我只能说如此精辟的解法要给一个大大的赞,使我对Promise的认识更深了一层。

    class Lazy {
        constructor(name) {
            this.sleepFirstTime = 0;
            this.promise = Promise.resolve().then(
                () => this.sleepFirstTime && this._sleep(this.sleepFirstTime)
            ).then(() => {
                console.log(`Hi! This is ${name}!`);
            });
        }
        sleepFirst(time) {
            this.sleepFirstTime = time;
            return this;
        }
        eat(food) {
            this.promise = this.promise.then(() => {
                console.log(`Eat ${food}~`);
            });
            return this;
        }
        sleep(time) {
            this.promise = this.promise.then(() => this._sleep(time));
            return this;
        }
        _sleep(time) {
            return new Promise((next) => {
                setTimeout(() => {
                    console.log(`Wake up after ${time}`);
                    next();
                }, time);
            });
        }
    }

    function LazyMan(name) {
        return new Lazy(name);
    }

重点代码解析

    constructor(name) {
        this.sleepFirstTime = 0;

        // Promise.resolve()创建一个Promise对象执行后面的函数
        this.promise = Promise.resolve().then(

        // this.sleepFirstTime为0时后面的函数不会执行
            () => this.sleepFirstTime && this._sleep(this.sleepFirstTime)
        ).then(() => {
            console.log(`Hi! This is ${name}!`);
        });
    }

假设执行该函数
LazyMan(“Hank”).sleepFirst(2000).sleep(2000).eat(“supper”)

【注意】构造函数初始化时仅执行了this.promise = Promise.resolve()
此时的this.promise仅仅是一个Promise对象
.then会在.eat("super")结束后才调用
然后.sleepFirst等函数对this.promise进行了改造
最后各种.then方法会链式调用达到所需效果

你可能感兴趣的:(前端面试,js)