JavaScript 链式结构序列化详解


Posted in Javascript onSeptember 30, 2016

一、概述

在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 相关文章推荐
扩展jquery实现客户端表格的分页、排序功能代码
Mar 16 Javascript
Extjs4 消息框去掉关闭按钮(类似Ext.Msg.alert)
Apr 02 Javascript
jQuery中json对象的复制方式介绍(数组及对象)
Jun 08 Javascript
jquery实现select选中行、列合计示例
Apr 25 Javascript
Javascript中With语句用法实例
May 14 Javascript
JavaScript DOM节点操作方法总结
Aug 23 Javascript
IScroll那些事_当内容不足时下拉刷新的解决方法
Jul 18 Javascript
react-native中ListView组件点击跳转的方法示例
Sep 30 Javascript
Angular4的输入属性与输出属性实例详解
Nov 29 Javascript
详解基于iview-ui的导航栏路径(面包屑)配置
Feb 22 Javascript
Vant picker 多级联动操作
Nov 02 Javascript
详解Vite的新体验
Feb 22 Javascript
Bootstrap3 Grid system原理及应用详解
Sep 30 #Javascript
CSS3 media queries结合jQuery实现响应式导航
Sep 30 #Javascript
微信小程序 LOL 英雄介绍开发实例
Sep 30 #Javascript
javascript的几种写法总结
Sep 30 #Javascript
Vue.js实现拖放效果的实例
Sep 30 #Javascript
PHP抓取HTTPS内容和错误处理的方法
Sep 30 #Javascript
Vue.js动态添加、删除选题的实例代码
Sep 30 #Javascript
You might like
php+dbfile开发小型留言本
2006/10/09 PHP
使用php语句将数据库*.sql文件导入数据库
2014/05/05 PHP
thinkphp实现发送邮件密码找回功能实例
2014/12/01 PHP
十个PHP高级应用技巧果断收藏
2015/09/25 PHP
PHP MYSQL实现登陆和模糊查询两大功能
2016/02/05 PHP
PHP页面跳转实现延时跳转的方法
2016/12/10 PHP
PHP正则表达式匹配替换与分割功能实例浅析
2017/02/04 PHP
PHP实现生成模糊图片的方法示例
2017/12/21 PHP
PHP防止sql注入小技巧之sql预处理原理与实现方法分析
2019/12/13 PHP
javascript 中对象的继承〔转贴〕
2007/01/22 Javascript
JavaScript入门教程 Cookies
2009/01/31 Javascript
jquery 打开窗口返回值实现代码
2010/03/04 Javascript
让人印象深刻的10个jQuery手风琴效果应用
2012/05/08 Javascript
5秒后跳转到另一个页面的js代码
2013/10/12 Javascript
用JS将搜索的关键字高亮显示实现代码
2013/11/08 Javascript
关于jQuery中的each方法(jQuery到底干了什么)
2014/03/05 Javascript
10分钟学会写Jquery插件实例教程
2014/09/06 Javascript
AngularJS入门教程(二):AngularJS模板
2014/12/06 Javascript
JavaScript+html5 canvas制作的百花齐放效果完整实例
2016/01/26 Javascript
微信小程序 动态传参实例详解
2017/04/27 Javascript
vue使用自定义icon图标的方法
2018/05/14 Javascript
微信小程序提取公用函数到util.js及使用方法示例
2019/01/10 Javascript
基于vue 动态菜单 刷新空白问题的解决
2020/08/06 Javascript
Vue实现简单计算器
2021/01/20 Vue.js
Python操作mongodb数据库进行模糊查询操作示例
2018/06/09 Python
django认证系统实现自定义权限管理的方法
2018/07/16 Python
python多线程调用exit无法退出的解决方法
2019/02/18 Python
python实现维吉尼亚算法
2019/03/20 Python
Win10环境python3.7安装dlib模块趟过的坑
2019/08/01 Python
Python3爬虫关于识别检验滑动验证码的实例
2020/07/30 Python
Python txt文件如何转换成字典
2020/11/03 Python
For Art’s Sake官网:手工制作的奢华眼镜
2018/12/15 全球购物
秋天的怀念教学反思
2014/04/28 职场文书
乒乓球兴趣小组活动总结
2014/07/08 职场文书
2015秋季幼儿园开学寄语
2015/03/25 职场文书
入党心得体会
2019/06/20 职场文书