javascript
flowJS源码个人分析
剛剛在騰訊云技術社區前端專欄中看到一篇騰訊高級前端工程師寫的《一個只有99行代碼的js流程框架》覺得很屌,感覺是將后臺的簡單的工作流思維搬到了前端js實現,本人不才在這里拜讀解析下源碼,而且經常有新手問我的很多問題其實是不懂如何調試一段js代碼,在這這里就詳細說明下我是怎么調試flowJS的源碼思路的。前端大神見笑小弟的總結
首先第一段:
var obj = arguments[0], flow = {init: function(){}}, flowData = {}, noop = function(){}, init = 'init', trace = flowJS.trace = flowJS.trace||[init];?
這段代碼就是把要用到的變量在開始聲明用逗號隔開,為了代碼的整潔和節省代碼的語句數量。
第二段:
if(({}).toString.call(obj) === '[object Object]'){//判斷第一個參數是否是對象,這個方法最準確extend(flow, obj);flow.init.call(extend({getCurr:function(){return init;}, stepData:function(dataName){return dataName?undefined:{};}, getPrev:noop, fail:noop, success:noop}, new Step(init)));}?
讀到extend(flow,obj);我們應該猜到是將參數和flow合并,我們在來看extend方法:
function prop(obj, fun){for(var p in obj) {obj.hasOwnProperty(p) && fun(p);//判斷這個屬性是原始屬性還是原型上的屬性}}/*對象的合并擴展*/function extend(des, src){prop(src, function(p){des[p] = src[p];});return des;}?
extend的方法是將src的原生屬性添加到des對象中。然后我們接著主線看:
flow.init.call(extend({getCurr:function(){return init;}, stepData:function(dataName){return dataName?undefined:{};}, getPrev:noop, fail:noop, success:noop}, new Step(init)));這段代碼就是要執行參數中的init方法并且將一個自定義添加Step(init)返回的對象中的屬性的對象作為init中的this對象。那重點在Step方法,但由于里面有點復雜剛開始接觸你看不到他在里什么的各種方法的用處,在這里我們最后結合《一個只有99行代碼的js流程框架》文章提供的api例子來調試里面各個方法的作用這樣更好的梳理整個功能的運行邏輯,在這里就體現了張鎮圳大神的所說的一個模塊的功能中代碼的邏輯梳理、可讀性、語義化多么重要,但flowJS這個框架可以理解為項目的在js原生上封裝了一層脫了業務層的框架,這種框架一般是組長或是高級前端工程來維護,而且一般隨著時間的積累慢慢成熟成型后期的改動會很小,所以這里只要相對用面向對象梳理下邏輯就可以,而業務層隨客戶的不斷增多,需求的改動頻繁出現,而且項目的大部分改動工作量都在業務層上,所以業務層代碼邏輯梳理、可讀性、語義化尤為重要,關系到后期維護的成本。
我們先從文章中第一個例子來調試;
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>flowJS demo1</title> </head> <body> </body> <script type="text/javascript" src="./flowJS.js"></script> <script type="text/javascript"> flowJS({init:function(){this.setNext('步驟A').setNext('步驟B').setNext('步驟C'); //預先設置好需要經過的每個步驟this.next();},'步驟A':function(){console.log('執行 步驟A');this.next(); //告訴流程執行下一步},'步驟B':function(){console.log('執行 步驟B');this.next();},'步驟C':function(){console.log('執行 步驟C');} }); </script> </html>?
第一例子中就兩個函數一個setNext,next;我們先來看setNext方法:
this.setNext = function(stepName, s, f){success = s||success;//獲取參數中成功回調函數fail = f||fail;//獲取參數中失敗回調函數nextStepName = stepName||nextStepName;//判斷stepName是否為空,為空將賦值原來的nextStep = new Step(nextStepName);//實例化對象return nextStep; };?
比較簡單,就是實例化step對象。之后我們在看next方法代碼:
this.next = function(stepName, s, f){nextStepName = stepName||nextStepName;success = s||success;fail = f||fail;nextStep = stepName?new Step(stepName):nextStep;if(nextStepName){nextStep.stepData = function(dataName){return dataName?nextData[dataName]:nextData;};nextStep.getPrev = function(){return name;};nextStep.fail = function(){fail.apply(this, arguments);};nextStep.success = function(){success.apply(this, arguments);};stepMapping[this.getCurr()] = true;if(allDone()){trace.push(nextStepName);typeof nextStepName == 'string' && proxy(nextStepName);if(({}).toString.call(nextStepName) === '[object Array]'){for(var i=0; i<nextStepName.length; i++){proxy(nextStepName[i]);}}function proxy(stepName, fn){if(typeof (fn = flow[stepName]) == 'function'){(function(fn, context){return function(){debugger;return fn.apply(context, arguments);};})(fn, extend({getCurr:function(){return stepName;}}, nextStep))();}else{throw new Error("step not found: "+stepName);}}}return nextStep;} };?
一開始懵逼看不懂很正常,我們在里面加斷點跟著demo一步一步走對整個邏輯的理解會更清晰一點。
this.next = function(stepName, s, f){debugger;nextStepName = stepName||nextStepName;success = s||success;fail = f||fail;nextStep = stepName?new Step(stepName):nextStep;if(nextStepName){nextStep.stepData = function(dataName){?
在里面加了debugger,然后我們一步一步的調試:
?
開始我覺得遺憾的是為什么nextStepName里的值是“步驟A”,整個方法是在這個地方調用的。
?
而nextStepName的變量是在setNext復制的
?
按道理應該里面的值是最后一次調用setNext的參數也就是“步驟C”,那為什么運行結果里面的值是步驟A呢,這里要看你的理解領悟能了,算法有了,結果有了,你能強行理解算法而經歷往結果靠攏么?這個問題的重點是在setNext的返回值,setNext返回的是新的step實例對象。在結合
this.setNext('步驟A').setNext('步驟B').setNext('步驟C'); //預先設置好需要經過的每個步驟?
用圖來解析這個方法的輸出和執行會更好理解:
?
this.next();?
這段代碼執行的this對象中的nextStepName私有變量的值為步驟A。
然后我們接著往下看next方法里的代碼:
success = s||success;//賦值參數的成功回調函數 fail = f||fail;//賦值參數的失敗回調函數 nextStep = stepName?new Step(stepName):nextStep;//如果第一參數有值的話返回這個值得step實例對象,沒有的話返回對象的nextStep私有變量 if(nextStepName){/*對象上添加一系列方法 暫時忽略*/nextStep.stepData = function(dataName){return dataName?nextData[dataName]:nextData;};nextStep.getPrev = function(){return name;};nextStep.fail = function(){fail.apply(this, arguments);};nextStep.success = function(){success.apply(this, arguments);};/*對象上添加一系列方法 暫時忽略*/stepMapping[this.getCurr()] = true;//標記上一步步驟被執行了?
代碼加了注釋基本很好理解就不多說了。下一個重點是這句
if(allDone()){? ?下回分解。
源碼可以在騰訊云技術社區上下載,我把源碼都上加注釋在放到Github上,下載暫時不放上去。
轉載于:https://www.cnblogs.com/libin-1/p/6708330.html
總結
以上是生活随笔為你收集整理的flowJS源码个人分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【IDEAEclipse】2、从Ecli
- 下一篇: Linux 文件夹含义(转)