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是单线程嘛。详情见(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之链式结构序列化
一.概述 在JavaScript中,链式模式代码,太多太多,如下: if_else: if(...){ //TODO }else if(...){ //TODO }else{ //TODO } swi ...
- JavaScript之链式结构序列化1
一.概述 在JavaScript中,链式模式代码,太多太多,如下: if_else: if(...){ //TODO }else if(...){ //TODO }else{ //TODO } swi ...
- javascript中=、==、===区别详解
javascript中=.==.===区别详解今天在项目开发过中发现在一个小问题.在判断n==""结果当n=0时 n==""结果也返回了true.虽然是个小问题 ...
- JavaScript对象的property属性详解
JavaScript对象的property属性详解:https://www.jb51.net/article/48594.htm JS原型与原型链终极详解_proto_.prototype及const ...
- javascript常用经典算法实例详解
javascript常用经典算法实例详解 这篇文章主要介绍了javascript常用算法,结合实例形式较为详细的分析总结了JavaScript中常见的各种排序算法以及堆.栈.链表等数据结构的相关实现与 ...
- NetBios 的结构体详解
[NetBios 的结构体详解] NetBIOS是早期的局域网传输协议. 1.结构体. 2.命令 NetBIOS命令的使用方式有两种,即等待和非等待(或称为同步与异步)方式. 如果命令码的高阶位是0时 ...
- C语言 队列 链式结构 实现
一个C语言链式结构实现的队列 mQueue (GCC编译). /** * @brief C语言实现的链式队列 * @author wid * @date 2013-10-31 * * @note 若代 ...
- C语言 栈 链式结构 实现
一个C语言链式结构实现的栈 mStack (GCC编译). /** * @brief C语言实现的链式结构类型的栈 * @author wid * @date 2013-10-30 * * @note ...
- C语言 线性表 双向链式结构 实现
一个双向链式结构实现的线性表 duList (GCC编译). /** * @brief 线性表双向链表结构 * @author wid * @date 2013-10-28 * * @note 若代码 ...
随机推荐
- 《OD学hadoop》Hadoop前置
一.Hadoop 前置课程 1. Linux系统,基本命令 2. Java语言,JavaSE相关知识 3. MySQL基本的DML和DDL SQL on Hadoop
- Android 用户界面---拖放(Drag and Drop)(三)
设计拖放操作 本节主要内容如下: 1. 如何开始拖拽: 2. 在拖拽期间如何响应事件: 3. 如何响应落下事件: 4. 如何结束拖放操作. 开始拖拽 用户使用一个拖拽手势开始拖拽,通常是在 ...
- SQLite及ORMlite在WebApp中的使用
Spring 配置 下面的databaseUrl在windows下,指向了c:/user/yourhome路径,暂时没想到怎么配置到WEBAPP根路径下. 因为是轻量级工控webapp,数据库规模不大 ...
- Excel 之查找与替换
Excel查找与替换 1,一旦学会查找,替换就简单了. 2,查找下面有一个选项,里面有对查找的范围进行限制 3,你可以选中一个区域,然后再查找,查找只会在你选择的区域里面进行 4,查找只能找特定值,而 ...
- Java自动装箱拆箱
一.装箱.拆箱定义 如果一个int型量被传递到需要一个Integer对象的地方,那么,编译器将在幕后插入一个对Integer构造方法的调用,这就叫做自动装箱.而如果一个Integer对象被放到需要in ...
- JSP的九个隐式(内置)对象
1.out 转译后对应JspWriter对象,其内部关联一个PrintWriter对象.是向客户端输出内容常用的对象. 2.request 转译后对应HttpServletRequest对象.客户端的 ...
- Linux进程调度策略
linux内核的三种主要调度策略: 1,SCHED_OTHER 分时调度策略, 2,SCHED_FIFO实时调度策略(先到先服务)3,SCHED_RR实时调度策略(时间片轮转) 实时进程将得到优先调用 ...
- Linux makefile教程之概述一[转]
概述—— 什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些 Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序员,makef ...
- Nginx 反向代理、负载均衡、页面缓存、URL重写、读写分离及简单双机热备详解
大纲 一.前言 二.环境准备 三.安装与配置Nginx (windows下nginx安装.配置与使用) 四.Nginx之反向代理 五.Nginx之负载均衡 (负载均衡算法:nginx负载算法 up ...
- Yii入门,登录
验证和授权在页面需要限制访问时用到.验证就是确认某人就是他所声称的那个人.通常涉及到用户名和密码,但也包含其他方式,例如智能卡,指纹等.授权是在验证用户后,查明他是否被允许管理指定的资源.通常判断他是 ...