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 --- Map集合結構詳解JavaScript
- JavaScript — Map集合結構詳解JavaScript
- 鏈式結構
- JavaScript學習總結(五)原型和原型鏈詳解JavaScript原型
- Go語言結構體鏈式程式設計與JSON序列化Go結構體程式設計JSON
- javascript原型和原型鏈詳解JavaScript原型
- 單鏈錶鏈式結構的建立
- 結構詳解
- 詳解 JavaScript 建構函式和 "new" 操作符JavaScript函式
- JavaScript函式模式詳解JavaScript函式模式
- 詳解JavaScript函式模式JavaScript函式模式
- javascript資料結構列表演算法詳解JavaScript資料結構演算法
- RNN 結構詳解RNN
- 棧鏈式結構簡單操作
- 建構函式詳解函式
- 佇列的鏈式儲存結構佇列
- JavaScript函式柯里化詳解JavaScript函式
- javascript的replace()函式用法詳解JavaScript函式
- javascript的sort()函式用法詳解JavaScript函式
- JavaScript 節流函式 Throttle 詳解JavaScript函式
- Javascript正規表示式詳解(一)JavaScript
- [JavaScript]原型、原型鏈、建構函式與繼承JavaScript原型函式繼承
- 最全總結 JavaScript Array 方法詳解JavaScript
- JavaScript 的 this 指向和繫結詳解JavaScript
- Java建構函式詳解Java函式
- JavaScript 函式引數解構物件JavaScript函式物件
- 詳解javascript立即執行函式表示式(IIFE)JavaScript函式
- MachO 檔案結構詳解Mac
- 資料結構:棧詳解資料結構
- Tomcat結構原理詳解Tomcat
- MySQL體系結構詳解MySql
- IP包頭結構詳解
- 線性表之鏈式儲存結構
- JavaScript陣列操作函式方法詳解JavaScript陣列函式
- 詳解Javascript中正規表示式的使用JavaScript
- 資料結構:順序結構和鏈式結構的資料型別定義資料結構資料型別
- Java序列化(Serializable)與反序列化詳解Java
- JavaScript學習總結(九)事件詳解JavaScript事件