当前位置:首页 > 系统运维

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

但是,这样有个缺点,我们是将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是单线程嘛。

那该怎么解决呢?

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

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要简约得多啦。

分享到:

滇ICP备2023006006号-16