JavaScript链式结构序列化详解

一、概述

创新互联公司坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站设计、成都做网站、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的黔江网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!

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

if_else:

 
 
  1. if(...){ 
  2.     //TODO 
  3. }else if(...){ 
  4.     //TODO 
  5. }else{ 
  6.     //TODO 

switch:

 
 
  1. switch(name){ 
  2.     case ...:{ 
  3.         //TODO 
  4.         break; 
  5.     } 
  6.     case ...:{ 
  7.         //TODO 
  8.         break; 
  9.     } 
  10.     default:{ 
  11.         //TODO     
  12.     } 

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

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

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

二、链式代码扁平化

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

 
 
  1. if(name === 'Monkey'){ 
  2.     console.log('yes, I am Monkey'); 
  3. }else if(name === 'Dorie'){ 
  4.     console.log('yes, I am Dorie'); 
  5. }else{ 
  6.     console.log('sorry, over for ending!'); 

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

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

 
 
  1. var thens = []; 
  2. thens.resolve = function(name){ 
  3.     for(var i = 0, len = this.length; i < len;i++){ 
  4.         if(this[i](name) !== 'next'){ 
  5.             break; 
  6.         } 
  7.     } 
  8. thens.push(f1, f2, f3); 

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

 
 
  1. function f1(name){ 
  2.     if(name === 'Monkey'){ 
  3.         console.log('yes, I am Monkey'); 
  4.     }else{ 
  5.         return 'next'; 
  6.     } 
  7. function f2(name){ 
  8.     if(name === 'Dorie'){ 
  9.         console.log('yes, I am Dorie'); 
  10.     }else{ 
  11.         return 'next'; 
  12.     } 
  13. function f3(){ 
  14.     console.log('sorry, over for ending!'); 

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

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

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

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

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

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

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

 
 
  1. thens.add = function(f){ 
  2.     if(typeof f === 'function'){ 
  3.         this.push(f); 
  4.         return this;         
  5.     }         

测试代码如下:

 
 
  1. var thens = []; 
  2. thens.add = function(f){ 
  3.     if(typeof f === 'function'){ 
  4.         this.push(f); 
  5.         return this;         
  6.     }         
  7. thens.resolve = function(name){ 
  8.     for(var i = 0, len = this.length; i < len;i++){ 
  9.         if(this[i](name) !== 'next'){ 
  10.             break; 
  11.         } 
  12.     }     
  13. thens.add(f1).add(f2).add(f3).resolve(); 

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

 
 
  1. function Slink(){ 
  2.     this.thens = []; 
  3.     this.thens.add = function(f){ 
  4.         if(typeof f === 'function'){ 
  5.             this.push(f); 
  6.             return this;         
  7.         }         
  8.     } 
  9.     this.thens.resolve = function(name){ 
  10.         for(var i = 0, len = this.length; i < len;i++){ 
  11.             if(this[i](name) !== 'next'){ 
  12.                 break; 
  13.             } 
  14.         }     
  15.     } 

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

 
 
  1. function Slink(){ 
  2.     this.thens = []; 
  3. Slink.prototype = { 
  4.     add: function(f){ 
  5.             if(typeof f === 'function'){ 
  6.                 this.thens.push(f); 
  7.                 return this;         
  8.             }         
  9.     }, 
  10.     resolve: function(name){ 
  11.             for(var i = 0, len = this.thens.length; i < len; i++){ 
  12.                 if(this.thens[i](name) !== 'next'){ 
  13.                     break; 
  14.                 } 
  15.             }     
  16.     } 

测试代码如下:

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

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

 
 
  1. function $go(f){ 
  2.     return new Slink(f); 
  3. function Slink(f){ 
  4.     this.thens = []; 
  5.     this.thens.push(f); 
  6. Slink.prototype = { 
  7.     add: function(f){ 
  8.             if(typeof f === 'function'){ 
  9.                 this.thens.push(f); 
  10.                 return this;         
  11.             }         
  12.     }, 
  13.     resolve: function(name){ 
  14.             for(var i = 0, len = this.thens.length; i < len; i++){ 
  15.                 if(this.thens[i](name) !== 'next'){ 
  16.                     break; 
  17.                 } 
  18.             }     
  19.     } 

测试代码如下:

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

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

 
 
  1. function _if(f){ 
  2.     return new Slink(f); 
  3. function Slink(f){ 
  4.     this.thens = []; 
  5.     this.thens.push(f); 
  6. Slink.prototype = { 
  7.     _elseIf: function(f){ 
  8.             if(typeof f === 'function'){ 
  9.                 this.thens.push(f); 
  10.                 return this;         
  11.             }         
  12.     }, 
  13.     _else: function(f){ 
  14.             return this._elseIf(f); 
  15.     }, 
  16.     resolve: function(name){ 
  17.             for(var i = 0, len = this.thens.length; i < len; i++){ 
  18.                 if(this.thens[i](name) !== 'next'){ 
  19.                     break; 
  20.                 } 
  21.             } 
  22.             return this;             
  23.     } 

测试代码如下:

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

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

 
 
  1. var func = Function.prototype; 
  2. func._else = func._elseIf = function(fn){ 
  3.     var _this = this; 
  4.     return function(){ 
  5.         var res = _this.apply(this,arguments); 
  6.         if(res==="next"){  //值为Boolean 
  7.             return fn.apply(this,arguments); 
  8.         } 
  9.         return res; 
  10.     } 

测试代码如下:

 
 
  1. function f1(name){ 
  2.     if(name === 'Monkey'){ 
  3.         console.log('yes, I am Monkey'); 
  4.     }else{ 
  5.         return 'next'; 
  6.     } 
  7. function f2(name){ 
  8.     if(name === 'Dorie'){ 
  9.         console.log('yes, I am Dorie'); 
  10.     }else{ 
  11.         return 'next'; 
  12.     } 
  13. function f3(){ 
  14.     console.log('sorry, over for ending!'); 
  15. f1._elseIf(f2)._else(f3)('Dorie'); 

三、异步代码链式扁平化

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

什么意思?如下:

 
 
  1. function f1(name){ 
  2.     setTimeout(function(){ 
  3.         if(name === 'Monkey'){ 
  4.             console.log('yes, I am Monkey'); 
  5.         }else{ 
  6.             return 'next'; 
  7.         } 
  8.     }, 2000); 
  9. function f2(name){ 
  10.     if(name === 'Dorie'){ 
  11.         console.log('yes, I am Dorie'); 
  12.     }else{ 
  13.         return 'next'; 
  14.     } 
  15. function f3(){ 
  16.     console.log('sorry, over for ending!'); 

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

测试代码如下:

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

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

Why?

因为JavaScript是单线程嘛。

那该怎么解决呢?

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

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

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

 
 
  1. function Slink(f){ 
  2.     this.thens = []; 
  3.     this.thens.push(f.bind(this)); 
  4. Slink.prototype = { 
  5.     _elseIf: function(f){ 
  6.             if(typeof f === 'function'){ 
  7.                 this.thens.push(f.bind(this)); 
  8.                 return this;        
  9.             }        
  10.     }, 
  11.     _else: function(f){ 
  12.             return this._elseIf(f.bind(this)); 
  13.     }, 
  14.     resolve: function(name, flag){ 
  15.             for(var i = flag, len = this.thens.length; i < len; i++){ 
  16.                 if(this.thens[i](name) !== 'next'){ 
  17.                     break; 
  18.                 } 
  19.             } 
  20.             return this;            
  21.     } 

测试代码如下:

 
 
  1. function f1(name){ 
  2.     setTimeout(function(){ 
  3.         if(name === 'Monkey'){ 
  4.             console.log('yes, I am Monkey'); 
  5.         }else{ 
  6.             //处理后续链 
  7.             this.resolve(name, 1);//1代表下一个需处理函数在数组中的位置 
  8.         } 
  9.     }.bind(this), 2000); 
  10. function f2(name){ 
  11.     if(name === 'Dorie'){ 
  12.         console.log('yes, I am Dorie'); 
  13.     }else{ 
  14.         return 'next'; 
  15.     } 
  16. function f3(){ 
  17.     console.log('sorry, over for ending!'); 
  18. _if(f1)._elseIf(f2)._else(f3).resolve('',0); 

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

是的,宗旨都一样,达到异步代码扁平化目的,不过这里的代码比Promise要简约得多啦。

网站名称:JavaScript链式结构序列化详解
本文地址:http://www.csdahua.cn/qtweb/news2/268252.html

网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网