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使用手册之二 DOM操作
Mar 24 Javascript
JS字符串截取函数实例
Dec 27 Javascript
JavaScript中的公有、私有、特权和静态成员用法分析
Nov 20 Javascript
jQuery动态背景图片效果实现方法
Jul 03 Javascript
基于jquery实现全屏滚动效果
Nov 26 Javascript
SpringMVC返回json数据的三种方式
Dec 10 Javascript
Jquery跨域获得Json的简单实例
May 18 Javascript
详解Bootstrap的iCheck插件checkbox和radio
Aug 24 Javascript
jQuery弹出层后禁用底部滚动条(移动端关闭回到原位置)
Aug 29 Javascript
利用JS判断字符串是否含有数字与特殊字符的方法小结
Nov 25 Javascript
vue2.0 实现页面导航提示引导的方法
Mar 13 Javascript
vue watch监控对象的简单方法示例
Jan 07 Vue.js
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统计字符串中中英文字符的个数
2013/06/23 PHP
解密ThinkPHP3.1.2版本之模板继承
2014/06/19 PHP
PHPer 需要了解的 5 个 Composer 小技巧
2014/08/18 PHP
PHP函数in_array()使用详解
2014/08/20 PHP
ThinkPHP中关联查询实例
2014/12/02 PHP
php获取twitter最新消息的方法
2015/04/14 PHP
YII Framework框架教程之安全方案详解
2016/03/14 PHP
javascript 动态添加事件代码
2008/11/30 Javascript
jquery imgareaselect 使用利用js与程序结合实现图片剪切
2009/07/30 Javascript
Ruffy javascript 学习笔记
2009/11/30 Javascript
JS常用表单验证方法总结
2014/05/22 Javascript
JS实现的仿淘宝交易倒计时效果
2015/11/27 Javascript
jQuery利用sort对DOM元素进行排序操作
2016/11/07 Javascript
canvas学习之API整理笔记(一)
2016/12/29 Javascript
Vue.js 插件开发详解
2017/03/29 Javascript
Vue组件内部实现一个双向数据绑定的实例代码
2019/04/04 Javascript
Vue 中使用富文本编译器wangEditor3的方法
2019/09/26 Javascript
python使用smtplib模块通过gmail实现邮件发送的方法
2015/05/08 Python
详解Python中 sys.argv[]的用法简明解释
2017/12/20 Python
python3使用smtplib实现发送邮件功能
2018/05/22 Python
python+splinter实现12306网站刷票并自动购票流程
2018/09/25 Python
python os.fork() 循环输出方法
2019/08/08 Python
解决TensorFlow模型恢复报错的问题
2020/02/06 Python
python+Selenium自动化测试——输入,点击操作
2020/03/06 Python
python产生模拟数据faker库的使用详解
2020/11/04 Python
南非领先的在线旅行社:Travelstart南非
2016/09/04 全球购物
Paper Cape官网:美国婴儿和儿童服装品牌
2019/11/02 全球购物
俄罗斯披萨、寿司和面食送货到家服务:2 Берега
2019/12/15 全球购物
药学专业个人自我评价
2013/11/11 职场文书
旅游专业职业生涯规划范文
2014/01/13 职场文书
入党积极分子自我鉴定范文
2014/03/25 职场文书
党员查摆剖析材料
2014/10/10 职场文书
武侯祠导游词
2015/02/04 职场文书
董事长开业致辞
2015/07/29 职场文书
2016年度先进班组事迹材料
2016/03/01 职场文书
MySQL事务的隔离级别详情
2022/07/15 MySQL