LazyMan面试题解析(十分经典的面试问题,考察面试者的综合编码能力)

综述:这是一道经典的面试题,综合考察面试者多方面的能力,走向高级前端之路必备,主要考察的点有:

 1.方法链式调用
 2.类的使用和面向对象编程的思路
 3.设计模式的应用,(发布-订阅者模式(也叫观察者模式)的应用)
 4.代码的解耦
 5.最少知识原则,也即迪米特法则(Law of Demeter),代码解耦,一个函数执行一个单一的功能,
 6.代码的书写结构和命名

1.题目如下

实现一个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

以此类推。

第一种方法,比较好维护,不便于理解:

 (function(window,undefined){
        var taskList = [];
        // 类
        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 subscribe(){
            var param = {},args = Array.prototype.slice.call(arguments);
            if(args.length < 1){
                throw new Error("subscribe 参数不能为空!");
            }
            param.msg = args[0];
            param.args = args.slice(1); // 函数的参数列表
            if(param.msg==="sleepFirst"){ //如果是第一个,则将其投入数组的第一个
                taskList.unshift(param);
            }else{
                taskList.push(param);
            }
        }

        // 发布者模式
        function publish(){
            if(taskList.length > 0){
                run(taskList.shift());//获取到当前事件队列中的第一个函数并执行
            }
        }

        // 鸭子叫
        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:;
            }
        }

        // 具体方法
        function lazyMan(str){
            console.log("Hi!This is "+ str +"!");
            publish();
        }

        function eat(str){
            console.log("Eat "+ str +"~");
            publish();
        }

        function sleep(num){
            setTimeout(function(){
                console.log("Wake up after "+ num);
                publish();
            }, num*1000);
        }

        function sleepFirst(num){
            setTimeout(function(){
                console.log("Wake up after "+ num);
                publish();
            }, num*1000);
        }


        // 暴露接口
        window.LazyMan = function(str){
            subscribe("lazyMan", str);
            setTimeout(function(){
                publish();
            }, 0);
            return new LazyMan();
        };
    })(window);
    /**
     *time/author:2019/4/19 "mouyao"
     *desc:三种调用方式如下,感受promise的代码编写逻辑
     */
    /* LazyMan("Hank").sleep(10).eat("dinner");
    LazyMan("Hank").eat("dinner").eat("supper");
    LazyMan("Hank").sleepFirst(5).eat("supper")*/
    

第二种实现方法:

这种代码更加便于理解,但是并不利于后期的维护。

/**
     *time/author:2019/4/19 "mouyao"
     *desc:另一种实现方式,这种方式更加容易理解,只是代码可维护性降低了
     */
    function _LazyMan(name){
        this.tasks = [];
        var self = this;
        var fn =(function(n){
            var name = n;
            return function(){
                console.log("Hi! This is " + name + "!");
                self.next();
            }
        })(name);
        this.tasks.push(fn);
        setTimeout(function(){
            self.next();
        }, 0); // 在下一个事件循环启动任务
    }
    /* 事件调度函数 */
    _LazyMan.prototype.next = function() {
        var fn = this.tasks.shift();
        fn && fn();
    };
    _LazyMan.prototype.eat=function(name){
        var self = this;
        //这种实现方法的核心点在这里,被使用了很多次,这里相当于是一个微任务,将这个函数存储到变量中,当需要使用的时候调用即可
        var fn =(function(name){
            return function(){
                console.log("Eat " + name + "~");
                self.next()
            }
        })(name);
        this.tasks.push(fn);
        return this; // 实现链式调用
    };
    _LazyMan.prototype.sleep=function(time){
        var self = this;
        var fn = (function(time){
            return function() {
                setTimeout(function(){
                    console.log("Wake up after " + time + "s!");
                    self.next();
                }, time * 1000);
            }
        })(time);
        this.tasks.push(fn);
        return this;
    };
    _LazyMan.prototype.sleepFirst = function(time) {
        var self = this;
        var fn = (function(time) {
            return function() {
                setTimeout(function() {
                    console.log("Wake up after"+time+"s!");
                    self.next();
                },time*1000);
            }
        })(time);
        this.tasks.unshift(fn);
        return this;
    };
    /* 封装 */
    function LazyMan(name){
        return new _LazyMan(name);
    }
     /*
        LazyMan("Hank").sleep(2).eat("dinner");
        LazyMan("Hank").eat("dinner").eat("supper");*/
        LazyMan("Hank").sleepFirst(5).eat("supper")



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值