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 相关文章推荐
JavaScript的Cookies
Jan 16 Javascript
一个用javascript写的select支持上下键、首字母筛选以及回车取值的功能
Sep 09 Javascript
如何确保JavaScript的执行顺序 之jQuery.html深度分析
Mar 03 Javascript
js 代码优化点滴记录
Feb 19 Javascript
一行命令搞定node.js 版本升级
Jul 20 Javascript
jQuery插件MixItUp实现动画过滤和排序
Apr 12 Javascript
JavaScript中Function函数与Object对象的关系
Dec 17 Javascript
利用jQuery插件imgAreaSelect实现图片上传裁剪(放大缩小)
Dec 02 Javascript
jQuery中select与datalist制作下拉菜单时的区别浅析
Dec 30 Javascript
解决vue跨域axios异步通信问题
Apr 17 Javascript
微信小程序实现卡片左右滑动效果的示例代码
May 01 Javascript
javascript实现前端分页功能
Nov 26 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 Memcache 中实现消息队列
2009/11/24 PHP
php警告Creating default object from empty value 问题的解决方法
2014/04/02 PHP
YII2框架中excel表格导出的方法详解
2017/07/21 PHP
Laravel5.1 框架控制器基础用法实例分析
2020/01/04 PHP
php中get_object_vars()在数组的实例用法
2021/02/22 PHP
javascript下判断一个对象是否具有指定名称的属性的的代码
2010/01/11 Javascript
$.get获取一个文件的内容示例代码
2013/09/11 Javascript
jquery cookie的用法总结
2013/11/18 Javascript
javascipt:filter过滤介绍及使用
2014/09/10 Javascript
javascript结合ajax读取txt文件内容
2014/12/05 Javascript
浅谈JavaScript对象与继承
2016/07/10 Javascript
Vue页面骨架屏的实现方法
2018/05/22 Javascript
webpack常用构建优化策略小结
2019/11/21 Javascript
JavaScript的垃圾回收机制与内存管理
2020/08/06 Javascript
vue 项目中当访问路由不存在的时候默认访问404页面操作
2020/08/31 Javascript
深入理解Python 代码优化详解
2014/10/27 Python
解析Python中的二进制位运算符
2015/05/13 Python
有关Python的22个编程技巧
2018/08/29 Python
Python里字典的基本用法(包括嵌套字典)
2019/02/27 Python
解决python gdal投影坐标系转换的问题
2020/01/17 Python
python nohup 实现远程运行不宕机操作
2020/04/16 Python
python自动化发送邮件实例讲解
2021/01/04 Python
英国领先的野生鸟类食品供应商:GardenBird
2018/08/09 全球购物
泰海淘:泰国king Power王权免税集团旗下跨境海淘综合型电商
2020/07/26 全球购物
2014年父亲节活动方案
2014/03/06 职场文书
食品销售计划书
2014/04/26 职场文书
九一八事变演讲稿
2014/09/05 职场文书
计划生育证明格式范本
2014/09/12 职场文书
2014购房个人委托书范本
2014/10/12 职场文书
2014年行政执法工作总结
2014/12/11 职场文书
2015年超市收银员工作总结
2015/04/25 职场文书
鸡毛信观后感
2015/06/11 职场文书
2015高中教师个人工作总结
2015/07/21 职场文书
Java并发编程必备之Future机制
2021/06/30 Java/Android
Python进程池与进程锁之语法学习
2022/04/11 Python
Apache SkyWalking 监控 MySQL Server 实战解析
2022/09/23 Servers