你的位置:首页 > Java教程

[Java教程]JavaScript之链式结构序列化


一、概述

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

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

测试代码如下:

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

View Code
三、异步代码链式扁平化

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

什么意思?如下:

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

View Code

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

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




去日本旅游跟团要多少钱去日本旅游需要多少钱日本旅游大概多少钱到日本旅游要花多少钱去日本签证要几天梅州灯会什么时间举办?梅州灯会时间地点? 北京欢乐谷地址在哪里?北京欢乐谷怎么去? 深圳欢乐谷春节门票多少钱?2015年深圳欢乐谷春节门票价格? 春节游丽江 错过这几件事枉去走一遭 阳山广东第一峰好玩不?广东第一峰旅游风景区团购价格? 九龙湖度假村团购票价?肇庆九龙湖度假村要门票吗? 肇庆九龙湖度假山庄介绍?九龙湖度假山庄有住宿吗? 鼎湖九龙湖酒店预订?九龙湖酒店多少钱一晚? 怀集燕岩风景图片?燕岩景区怎么走? 从化碧泉酒店房间价格?广州从化碧泉大酒店住宿攻略? 从化碧水湾温泉度假村简介?从化逸泉国际大酒店电话? 从化碧水湾温泉度假村怎么走?从化逸泉国际大酒店地址? 邮轮旅游特色 西岛在哪里_西岛在哪个省_西岛怎么去 新西兰续缘霍比特人,史上最全指环王取景拍摄地旅游指南 2012年11月映汶高速正式通车_都汶高速全线贯通 M04-0911GC Datasheet M04-0911GC Datasheet M04-0911MBC Datasheet M04-0911MBC Datasheet M04-0911QBC Datasheet M04-0911QBC Datasheet 雁门关在哪里 雁门关在哪里 雁门关在哪里 青岛海底世界门票价格 青岛海底世界门票价格 青岛海底世界门票价格 黄山在哪 黄山在哪 黄山在哪