• JavaScript之链式结构序列化1


    一、概述

    在JavaScript中,链式模式代码,太多太多,如下:

    if_else:

    复制代码
    if(...){
        //TODO
    }else if(...){
        //TODO
    }else{
        //TODO
    }
    复制代码

    switch:

    复制代码
    switch(name){
        case ...:{
            //TODO
            break;
        }
        case ...:{
            //TODO
            break;
        }
        default:{
            //TODO    
        }
    }
    复制代码

    疑问:诸如上述这些链式代码,倘若,我们想将其扁平化链式处理呢?如下:

    //fn1,f2,f3为处理函数
    _if(fn1)._elseIf(fn2)._else(fn3);

    下面我们就来一起尝试实现下呗。

    二、链式代码扁平化

    假如,现在我们有如下链式代码:

    复制代码
    if(name === 'Monkey'){
        console.log('yes, I am Monkey');
    }else if(name === 'Dorie'){
        console.log('yes, I am Dorie');
    }else{
        console.log('sorry, over for ending!');
    }
    复制代码

    好了,现在我们一步一步将其"扁平化"。

    其实看看上面的代码,不难发现,if…else这种格式,其实就是数据结构中的单链表,那么,初步利用JavaScript实现单链表,如下:

    复制代码
    var thens = [];
    thens.resolve = function(name){
        for(var i = 0, len = this.length; i < len;i++){
            if(this[i](name) !== 'next'){
                break;
            }
        }
    }
    thens.push(f1, f2, f3);
    复制代码

    其中f1,f2,f3为判断函数,并且我们假设,如果诸如f1、f2、f3返回’next’时,就继续往下查找,否则,停止往下查找。如下:

    复制代码
    function f1(name){
        if(name === 'Monkey'){
            console.log('yes, I am Monkey');
        }else{
            return 'next';
        }
    }
    function f2(name){
        if(name === 'Dorie'){
            console.log('yes, I am Dorie');
        }else{
            return 'next';
        }
    }
    function f3(){
        console.log('sorry, over for ending!');
    }
    复制代码

    好了,这就是链表的模式。

    但是,我们的最终目的是想实现如下这样的呢?

    //fn1,f2,f3为处理函数
    _if(fn1)._elseIf(fn2)._else(fn3);

    你可能会说,将上述代码改成如下这样,不就好了吗?!!

    thens.push(f1).push(f2).push(f3).resolve();

    But,JavaScript的push方法返回的是数组的新长度,而不是数组对象哦。

    So,那我们只能新写一个add方法,效果和push一样,但是返回数组对象。如下:

    复制代码
    thens.add = function(f){
        if(typeof f === 'function'){
            this.push(f);
            return this;        
        }        
    }
    复制代码

    测试代码如下:

     View Code

    但是,这样有个缺点,我们是将add、resolve方法绑定在全局变量thens中的,总不能每次创建一个数组时,都复制粘贴一遍方法吧,所以重构代码如下:

    复制代码
    function Slink(){
        this.thens = [];
        this.thens.add = function(f){
            if(typeof f === 'function'){
                this.push(f);
                return this;        
            }        
        }
        this.thens.resolve = function(name){
            for(var i = 0, len = this.length; i < len;i++){
                if(this[i](name) !== 'next'){
                    break;
                }
            }    
        }
    }
    复制代码

    显然,add,resolve这种公共方法,在每次实例化时,都创建一遍是不科学的,so,利用prototype在原有的基础上继续变形,如下:

    复制代码
    function Slink(){
        this.thens = [];
    }
    Slink.prototype = {
        add: function(f){
                if(typeof f === 'function'){
                    this.thens.push(f);
                    return this;        
                }        
        },
        resolve: function(name){
                for(var i = 0, len = this.thens.length; i < len; i++){
                    if(this.thens[i](name) !== 'next'){
                        break;
                    }
                }    
        }
    }
    复制代码

    测试代码如下:

    var thens = new Slink();
    thens.add(f1).add(f2).add(f3);
    thens.resolve();

    不错,但是这样,我们每次都得手动new一个Slink,有点麻烦,所以,我们将new Slink这个过程,封装到函数中,如同jQuery一样,如下:

    复制代码
    function $go(f){
        return new Slink(f);
    }
    function Slink(f){
        this.thens = [];
        this.thens.push(f);
    }
    Slink.prototype = {
        add: function(f){
                if(typeof f === 'function'){
                    this.thens.push(f);
                    return this;        
                }        
        },
        resolve: function(name){
                for(var i = 0, len = this.thens.length; i < len; i++){
                    if(this.thens[i](name) !== 'next'){
                        break;
                    }
                }    
        }
    }
    复制代码

    测试代码如下:

    $go(f1).add(f2).add(f3).resolve();

    好了,大功告成,接下来就是语法糖滴问题咯,整理代码如下:

    复制代码
    function _if(f){
        return new Slink(f);
    }
    function Slink(f){
        this.thens = [];
        this.thens.push(f);
    }
    Slink.prototype = {
        _elseIf: function(f){
                if(typeof f === 'function'){
                    this.thens.push(f);
                    return this;        
                }        
        },
        _else: function(f){
                return this._elseIf(f);
        },
        resolve: function(name){
                for(var i = 0, len = this.thens.length; i < len; i++){
                    if(this.thens[i](name) !== 'next'){
                        break;
                    }
                }
                return this;            
        }
    }
    复制代码

    测试代码如下:

    _if(f1)._elseIf(f2)._else(f3).resolve();

    当然,除开利用数组这种方式,还可以利用闭包,实现链式扁平化效果,如下:

    复制代码
    var func = Function.prototype;
    func._else = func._elseIf = function(fn){
        var _this = this;
        return function(){
            var res = _this.apply(this,arguments);
            if(res==="next"){  //值为Boolean
                return fn.apply(this,arguments);
            }
            return res;
        }
    }
    复制代码

    测试代码如下:

    var thens = [];
    thens.add = function(f){
    if(typeof f === 'function'){
    this.push(f);
    return this;
    }
    }
    thens.resolve = function(name){
    for(var i = 0, len = this.length; i < len;i++){
    if(this[i](name) !== 'next'){
    break;
    }
    }
    }
    thens.add(f1).add(f2).add(f3).resolve();

    复制代码
    function f1(name){
        if(name === 'Monkey'){
            console.log('yes, I am Monkey');
        }else{
            return 'next';
        }
    }
    function f2(name){
        if(name === 'Dorie'){
            console.log('yes, I am Dorie');
        }else{
            return 'next';
        }
    }
    function f3(){
        console.log('sorry, over for ending!');
    }
    f1._elseIf(f2)._else(f3)('Dorie');
    复制代码
    三、异步代码链式扁平化

    在上面我们讨论的都是同步过程,倘若,链式调用函数中有异步情况呢?

    什么意思?如下:

    复制代码
    function f1(name){
        setTimeout(function(){
            if(name === 'Monkey'){
                console.log('yes, I am Monkey');
            }else{
                return 'next';
            }
        }, 2000);
    }
    function f2(name){
        if(name === 'Dorie'){
            console.log('yes, I am Dorie');
        }else{
            return 'next';
        }
    }
    function f3(){
        console.log('sorry, over for ending!');
    }
    复制代码

    我们将f1利用setTimeout变成了异步,按照上述代码的逻辑,应该是等f1完全执行完毕(包括setTimeout执行)后,判断是否执行f2,但真的如此吗?

    测试代码如下:

    _if(f1)._elseIf(f2)._else(f3).resolve();

    执行代码的结果就是,什么也不输出。

    Why?

    因为JavaScript是单线程嘛。详情见(here

    那该怎么解决呢?

    由于有异步代码,且必须在异步代码后处理后续的链,那么我们就等待异步代码执行完毕后,才执行后续的链嘛,如下:

    复制代码
    function f1(name){
        setTimeout(function(){
            if(name === 'Monkey'){
                console.log('yes, I am Monkey');
            }else{
                //处理后续链
                this.resolve(name, 1);//1代表下一个需处理函数在数组中的位置
            }
        }.bind(this), 2000);
    }
    复制代码

    好了,由于在函数中,我们使用了this,其代表Slink对象,且改变了resolve方法,固,需细微调整Slink构造函数和原型链,如下:

    复制代码
    function Slink(f){
        this.thens = [];
        this.thens.push(f.bind(this));
    }
    Slink.prototype = {
        _elseIf: function(f){
                if(typeof f === 'function'){
                    this.thens.push(f.bind(this));
                    return this;        
                }        
        },
        _else: function(f){
                return this._elseIf(f.bind(this));
        },
        resolve: function(name, flag){
                for(var i = flag, len = this.thens.length; i < len; i++){
                    if(this.thens[i](name) !== 'next'){
                        break;
                    }
                }
                return this;            
        }
    }
    复制代码

    测试代码如下:

    function f1(name){
    setTimeout(function(){
    if(name === 'Monkey'){
    console.log('yes, I am Monkey');
    }else{
    //处理后续链
    this.resolve(name, 1);//1代表下一个需处理函数在数组中的位置
    }
    }.bind(this), 2000);
    }
    function f2(name){
    if(name === 'Dorie'){
    console.log('yes, I am Dorie');
    }else{
    return 'next';
    }
    }
    function f3(){
    console.log('sorry, over for ending!');
    }
    _if(f1)._elseIf(f2)._else(f3).resolve('',0);


    复制代码
    function f1(name){
        setTimeout(function(){
            if(name === 'Monkey'){
                console.log('yes, I am Monkey');
            }else{
                //处理后续链
                this.resolve(name, 1);//1代表下一个需处理函数在数组中的位置
            }
        }.bind(this), 2000);
    }
    function f2(name){
        if(name === 'Dorie'){
            console.log('yes, I am Dorie');
        }else{
            return 'next';
        }
    }
    function f3(){
        console.log('sorry, over for ending!');
    }
    _if(f1)._elseIf(f2)._else(f3).resolve('',0);
    复制代码

    哈哈,如果你了解Promise,是不是感觉这么相似呢。

    是的,宗旨都一样,达到异步代码扁平化目的,不过这里的代码比Promise要简约得多啦。关于Promise详情见(here)。

    如果喜欢本文,请点击右下角的推荐,谢谢~
  • 相关阅读:
    【博弈论】K倍动态减法游戏
    【博弈论】翻硬币游戏8种模型
    P4124 [CQOI2016]手机号码(数位DP,前导0)
    AtCoder Beginner Contest 146
    Sumitomo Mitsui Trust Bank Programming Contest 2019
    ICPC 2018 Nanjing Regional
    模拟退火基础学习&模板
    AtCoder Beginner Contest 117
    AtCoder Beginner Contest 118
    AtCoder Beginner Contest 119
  • 原文地址:https://www.cnblogs.com/libin-1/p/5918812.html
Copyright © 2020-2023  润新知