一、概述

在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;
    }
}

测试代码如下:

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();

但是,这样有个缺点,我们是将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;
    }
}

测试代码如下:

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);

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

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

JavaScript 链式结构序列化详解的更多相关文章

  1. JavaScript之链式结构序列化

    一.概述 在JavaScript中,链式模式代码,太多太多,如下: if_else: if(...){ //TODO }else if(...){ //TODO }else{ //TODO } swi ...

  2. JavaScript之链式结构序列化1

    一.概述 在JavaScript中,链式模式代码,太多太多,如下: if_else: if(...){ //TODO }else if(...){ //TODO }else{ //TODO } swi ...

  3. javascript中=、==、===区别详解

    javascript中=.==.===区别详解今天在项目开发过中发现在一个小问题.在判断n==""结果当n=0时 n==""结果也返回了true.虽然是个小问题 ...

  4. JavaScript对象的property属性详解

    JavaScript对象的property属性详解:https://www.jb51.net/article/48594.htm JS原型与原型链终极详解_proto_.prototype及const ...

  5. javascript常用经典算法实例详解

    javascript常用经典算法实例详解 这篇文章主要介绍了javascript常用算法,结合实例形式较为详细的分析总结了JavaScript中常见的各种排序算法以及堆.栈.链表等数据结构的相关实现与 ...

  6. NetBios 的结构体详解

    [NetBios 的结构体详解] NetBIOS是早期的局域网传输协议. 1.结构体. 2.命令 NetBIOS命令的使用方式有两种,即等待和非等待(或称为同步与异步)方式. 如果命令码的高阶位是0时 ...

  7. C语言 队列 链式结构 实现

    一个C语言链式结构实现的队列 mQueue (GCC编译). /** * @brief C语言实现的链式队列 * @author wid * @date 2013-10-31 * * @note 若代 ...

  8. C语言 栈 链式结构 实现

    一个C语言链式结构实现的栈 mStack (GCC编译). /** * @brief C语言实现的链式结构类型的栈 * @author wid * @date 2013-10-30 * * @note ...

  9. C语言 线性表 双向链式结构 实现

    一个双向链式结构实现的线性表 duList (GCC编译). /** * @brief 线性表双向链表结构 * @author wid * @date 2013-10-28 * * @note 若代码 ...

随机推荐

  1. [HIHO1300]展胜地的鲤鱼旗(栈,dp)

    题目链接:http://hihocoder.com/problemset/problem/1300 给一个字符串,只包含'('和')',问存在多少个子串似的括号是匹配的. 匹配规则在题干中描(蒻)述( ...

  2. C++关键字 inline

    在C&C++中 一.inline关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义. 表达式形式的宏定义一例: #define ExpressionName(Va ...

  3. HDU 1548 (最基础的BFS了) A strange lift

    这是一维的BFS,而且没有什么变形,应该是最基础的BFS了吧 题意: 有这样一个奇葩的电梯,你在第i层的时候你只能选择上或者下Ki层,也就是你只能从第i层到达i+Ki或者i-Ki层.当然电梯最低只能在 ...

  4. HDU 1051 Wooden Sticks【LIS】

    题意:给出n个木头的重量wi,长度li,如果满足w[i+1]>=w[i]且l[i+1]>=l[i],则不用耗费另外的加工时间,问至少需要多长时间加工完这些木头. 第一次做这一题目也没有做出 ...

  5. o4.数组指针和指针数组的区别

    ------- android培训.iOS培训.期待与您交流! ---------- 我们看一下数组指针和指针数组: 数组指针(也称行指针)定义 int (*p)[n];()优先级高,首先说明p是一个 ...

  6. 解决mysql中文乱码(总)

    转自:http://hi.baidu.com/cnvienna/blog/item/2e99efdfd94fc61862279801.html MySQL会出现中文乱码的原因不外乎下列几点: .ser ...

  7. 2015-10-09 Fri 晴 加快进度看书

    最近老感觉每天不够用,每天7点起来,吃饭完了8点开始看书,不知道是我看书太慢了还是时间过得真的很快,不知不觉中午就到了.而这个时候我才看2章的内容,下午能多看3章内容.一本书也就一天的时候,而我现在还 ...

  8. liux环境下配置jdk

    大家都知道,现在JAVA的发展可谓是如日中天,它覆盖面非常广泛,小到个人PC,大到商业应用都能见到它的身影.以前它是由SUN公司来维护的,现在已经归属到甲骨文旗下了. 今天我们来学习一下Java JD ...

  9. 为什么要用Java泛型

    啥是泛型? 泛型(generic)是指参数化类型的能力.可以定义带泛型类型的类或方法,随后编译器会用具体的类型来代替它. 举个栗子 上述代码在编译期没有问题,但在运行期,将会报错.就是因为List的a ...

  10. 【转】lua Date和Time

    time和date两个函数在Lua中实现所有的时钟查询功能.函数time在没有参数时返回当前时钟的数值.(在许多系统中该数值是当前距离某个特定时间的秒数.)当为函数调用附加一个特殊的时间表时,该函数就 ...