摘要:動態(tài)數(shù)據(jù)的區(qū)別是數(shù)據(jù)的變動性,可能是用戶操作構(gòu)造,可能是服務(wù)端查詢數(shù)據(jù)返回,可能是本地緩存需要反復(fù)更新修改的數(shù)據(jù)等等。
程序員總是在做重復(fù)性的工作,常常因為80%公用的內(nèi)容,但有20%的不同之處,導(dǎo)致要重寫,或復(fù)制修改;
更好的共用化封裝是程序員不斷追求的目標(biāo),設(shè)計的公用性與適用度還有效率之間要找平衡點;
舉些例子,分享給新手!(示例來自我的 fixedstar 引擎)
1. 附加功能封包輔助功能設(shè)計盡可能不影響原函數(shù)設(shè)計,也支持原函數(shù)的變動升級;
只關(guān)注輔助功能本身使用場景,不受業(yè)務(wù)應(yīng)用場景限制;
單一功能實現(xiàn),不要多功能集成;
示例1:高頻觸發(fā)的行為常常會產(chǎn)生不必要的消耗,可以設(shè)計節(jié)流函數(shù)來控制使用頻率。
/** * 獲取在控制執(zhí)行頻率的節(jié)流函數(shù) * * - 與上一次執(zhí)行時間超過等待時間時可以直接觸發(fā); * - 間隔時間之內(nèi)調(diào)用不會觸發(fā),保留最后一次調(diào)用定時在等待時間后觸發(fā); * * 可用于高頻發(fā)事件的回調(diào),如:onresize onscroll 等等 * * @param {function} cb 回調(diào) * @param {object} [thisObj] 回調(diào)函數(shù)的this對象 * @param {number} [wait=0] 設(shè)置兩次執(zhí)行間隔最小等待時間,沒有設(shè)置時間則為下一幀解鎖 * @return {function} 有限制的回調(diào) */ function throttle(cb, thisObj, wait) { if( wait===undefined ) wait = 0; var timer = null; var locked = false; var isfirst = true; var now; return function () { var args = arguments; var _this = this; // 定時執(zhí)行 var fn = function () { locked = false; isfirst = false; now = Date.now(); timer = setTimeout(function () { locked = false; timer = null; isfirst = true; }, wait); cb.apply(thisObj || _this, args); }; // 如果鎖了,說明前面有調(diào)用并且沒有到兩次間隔最小執(zhí)行等待時間 if(locked) { clearTimeout(timer); timer = null; // 如果到了最大等待時間,直接執(zhí)行,并延時等待 if( Date.now()-now>=wait ) { fn(); now = Date.now(); } } else { // 沒有鎖定的第一次直接執(zhí)行,并延時等待 if( isfirst ) fn(); // 在第一次或執(zhí)行后的第一次調(diào)用中,進(jìn)行鎖定 now = Date.now(); locked = true; } // 如果沒有定時任務(wù)加上 if( !timer ) timer = setTimeout(fn, wait-(Date.now()-now)); }; };
上例中代碼重新編輯過,setTimeout 會影響效率,原代碼中使用 thread 進(jìn)行調(diào)用,此處為了方便演示,暫使用 setTimeout 替換,關(guān)于 thread 有時間再開新貼。
調(diào)用:
var t, st = 0; var fn = fx.object.throttle(function (i) { var now = Date.now(); console.log(i, "ok", now-st); st = now; }, null, 30, 2000); // 下面代碼只執(zhí)行 1和4,因為1是直接調(diào)用,2、3、4都在1調(diào)用之后沒有到間隔時間會一個個覆蓋,最后留下來是4,會定時間隔執(zhí)行 fn(1);fn(2);fn(3);fn(4); // 測試高頻率10ms時,仍然按照30ms間隔進(jìn)行觸發(fā) t = setInterval(fn, 10); //clearInterval(t); // 測試較低頻率50ms時,會按照50ms間隔進(jìn)行觸發(fā) //t = setInterval(fn, 50); //clearInterval(t);示例2:
如通常做優(yōu)化時,需要計算代碼運行時間,工具有時不能很細(xì)致也受到環(huán)境限制,所以不可避免需要寫一些計時的腳本調(diào)試或統(tǒng)計:
/** * 生成一個計算函數(shù)運行時間的新函數(shù)(可以打印或使用回調(diào)函數(shù)返回運行時間值) * * 新函數(shù)與原函數(shù)調(diào)用及功能不變; * * @param {function} cb 測試函數(shù) * @param {object} [thisObj=cb.this] 測試函數(shù)的 this,如果參數(shù)為 undefined 時 this 為函數(shù)內(nèi)部綁定的this * @param {function} [timeCb] 用來獲取運行時間回調(diào)函數(shù) * @param {number} [timeCb.time] 運行時間 ms * @param {number} [timeCb.cb] 原函數(shù) * @param {...} [timeCb.args] 多參數(shù),傳入原函數(shù)的參數(shù) * @return {function} 返回cb原功能函數(shù)增加了計算功能后封包的函數(shù) */ function runTime(cb, thisObj, timeCb) { var t; return function () { var args = Array.prototype.slice.call(arguments); t = Date.now(); if( thisObj===undefined ) thisObj = this; var ret = cb.apply(thisObj, args); var v = Date.now()-t; if( timeCb ) { timeCb.apply(null, [v, cb].concat(args)); } else { console.log(cb.name, "runTime:", v, "ms"); } return ret; }; };
調(diào)用:
function sum(n) { var m = 0; for (var i = 0; i < n; i++) { m+=i; } return m; } function getRunTime(time) { if( time<1000 ) { console.log("運行時間1秒內(nèi)可以接受", time); } else { console.warn("運行時間超過了1秒不能接受", time); } } var sum2 = runTime(sum); // 臨時調(diào)試打印結(jié)果 console.log(sum2(100000000)); // 需要獲取時間處理相關(guān)邏輯 var sum3 = runTime(sum, null, getRunTime); sum3(100000000); sum3(5000000000); // 如果不想在生產(chǎn)時使用,可以設(shè)計運行條件,或在gulp生成時排除 sum4 = window.isDebug ? runTime(sum) : sum; sum4(100000000);2. 通用的動態(tài)處理
通常獲取數(shù)據(jù)屬性都是靜態(tài)的,也就是 o.x 類似這樣,但當(dāng)數(shù)據(jù)有一定不確定性時,如 o.point.x 這時如果 o.point 有可能為 undefined 時,就會報錯 Cannot read property "x" of undefined;
有人可能會問,為什么不將數(shù)據(jù)構(gòu)造好,避免這樣的情況呢!或在發(fā)現(xiàn)問題補(bǔ)上數(shù)據(jù)即可...
上面說的是靜態(tài)數(shù)據(jù)的處理方式,如系統(tǒng)配置數(shù)據(jù)。動態(tài)數(shù)據(jù)的區(qū)別是數(shù)據(jù)的變動性,可能是用戶操作構(gòu)造,可能是服務(wù)端查詢數(shù)據(jù)返回,可能是本地緩存需要反復(fù)更新修改的數(shù)據(jù)等等。
動態(tài)數(shù)據(jù)會產(chǎn)生較多結(jié)構(gòu)設(shè)計固定,但實際內(nèi)容不確定,或一定要寫成固定式的就會增加很多結(jié)構(gòu)維護(hù)消耗。如:
var users = { user1: { info: { hobby: ["玩游戲", "藍(lán)球", "看電影"] }, // ... }, user2: { info: { hobby: [] }, // ... }, user3: {} // ... };
如果業(yè)務(wù)需求要將所有沒有填寫愛好的用戶收集起來,常規(guī)判斷方法是:
(試想一下,再增加些寫數(shù)據(jù)的需求,復(fù)雜度就更高了,不僅需要判斷對象存在性,還要一層層創(chuàng)建數(shù)據(jù),如將填寫了愛好的用戶增加系統(tǒng)動態(tài)評分等等)
var ret = []; for( var k in users ) { var user = users[k]; if( user && user.info && user.info.hobby ) { var fristHobby = user.info.hobby[0]; if( fristHobby ) ret.push(fristHobby); } } console.log(ret);
如果你習(xí)慣這樣的多重判斷,反復(fù)出現(xiàn)或在不同業(yè)務(wù)邏輯中返回硬性封裝而不感到厭煩,那就可以跳過此文。
實際我們想一次能拿到屬性,理想調(diào)用如下:
var ret = []; for( var k in users ) { var fristHobby = attr(users, k + ".info.hobby[0]"); if( fristHobby ) ret.push(fristHobby); } console.log(ret);
代碼實現(xiàn):
/** * 獲取或設(shè)置對象屬性,允許多級自動創(chuàng)建 * * @param {object} obj 需要屬性的對象,通常操作JSON * @param {String} name 屬性名稱(.[]是關(guān)鍵字不可以命名),可以多級,如:name 或 msg.title 或 msg.list[0].user * @param value 賦值 * @return 返回對象屬性,未找到返回 undefined */ function attr(obj, name, value) { if (!obj || typeof obj != "object") return undefined; var pAry = name.split("."); var key = null; var isSet = (arguments.length > 2); for (var i = 0; i < pAry.length; i++) { key = pAry[i].trim(); // 如果此鍵為數(shù)組,先要指向數(shù)組元素 if (/^[^[]+[d+]$/.test(key)) { var tempAry = key.split(/[[]]/g); key = tempAry[0]; if (!obj[key]) { if (!isSet) return undefined; obj[key] = []; } obj = obj[key]; key = parseInt(tempAry[1]); } // 然后判斷是否最后一級,則直接賦值 結(jié)束 if (i >= pAry.length - 1) { if (!isSet) return obj[key]; obj[key] = value; break; } // 否則還有下級,則指向下級對象 if (!obj[key] || typeof obj[key] != "object") { if (!isSet) return undefined; obj[key] = {}; } obj = obj[key]; } return value; };
使用這個封裝完成用戶增加系統(tǒng)動態(tài)評分就簡單了,此方法支持多級寫數(shù)據(jù):
var ret = []; for( var k in users ) { var n = 0; var fristHobby = attr(users, k + ".info.hobby[0]"); if( fristHobby ) { ret.push(fristHobby); n++; } var score = attr(users, k + ".info.score"); attr(users, k + ".info.score", score ? score + n : n); } console.log(ret, users);更多例子
未完待續(xù)...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/81141.html
摘要:前戲補(bǔ)上參會的完整記錄,這個問題從一開始我就是準(zhǔn)備自問自答的,希望可以通過這種形式把大會的干貨分享給更多人。 showImg(http://7xqy7v.com1.z0.glb.clouddn.com/colorful/blog/feday2.png); 前戲 2016/3/21 補(bǔ)上參會的完整記錄,這個問題從一開始我就是準(zhǔn)備自問自答的,希望可以通過這種形式把大會的干貨分享給更多人。 ...
摘要:二面向?qū)ο笥惺裁刺卣髅嫦驅(qū)ο蟮闹饕卣饔谐橄罄^承封裝和多態(tài)。析構(gòu)函數(shù)析構(gòu)函數(shù)是在引入的,它的作用與調(diào)用時機(jī)和構(gòu)造函數(shù)剛好相反,它在對象被銷毀時自動執(zhí)行。 PHP面試專欄正式起更,每周一、三、五更新,提供最好最優(yōu)質(zhì)的PHP面試內(nèi)容。PHP中面向?qū)ο蟪?嫉闹R點有以下7點,我將會從以下幾點進(jìn)行詳細(xì)介紹說明,幫助你更好的應(yīng)對PHP面試常考的面向?qū)ο笙嚓P(guān)的知識點和考題。整個面向?qū)ο笪恼碌慕Y(jié)構(gòu)涉...
摘要:函數(shù)柯里化是把支持多個參數(shù)的函數(shù)變成接收單一參數(shù)的函數(shù),并返回一個函數(shù)能接收處理剩余參數(shù),而反柯里化就是把參數(shù)全部釋放出來。但在一些復(fù)雜的業(yè)務(wù)邏輯封裝中,函數(shù)柯里化能夠為我們提供更好的應(yīng)對方案,讓我們的函數(shù)更具自由度和靈活性。 showImg(https://segmentfault.com/img/bVburN1?w=800&h=600); 柯里化(Curring, 以邏輯學(xué)家Has...
摘要:目前,比特幣使用的是來進(jìn)行交易簽名,并且在共識協(xié)議中使用了哈希算法。盡管的實現(xiàn)提供的是最流行的加密算法,但我們鼓勵社區(qū)提供更優(yōu)化的加密算法實現(xiàn)以減少運行時開銷。 Nervos 底層公鏈 CKB 的虛擬機(jī)(CKB-VM)是基于 RISC-V 指令集打造的區(qū)塊鏈虛擬機(jī)。在上一堂分享中,我們簡單介紹了區(qū)塊鏈虛擬機(jī),以及我們理想中的區(qū)塊鏈虛擬機(jī)的樣子。在本篇文章中,CKB-VM 設(shè)計者將詳細(xì)的...
摘要:能源行業(yè)數(shù)字化轉(zhuǎn)型方向與人才培養(yǎng)大數(shù)據(jù)微軟雅黑全球能源行業(yè)數(shù)字化轉(zhuǎn)型的使命是實現(xiàn)敏捷能源。微軟雅黑油氣行業(yè)數(shù)字化轉(zhuǎn)型聚焦生產(chǎn)服務(wù)技術(shù)新業(yè)務(wù)運營人才生態(tài)七大重點方向,結(jié)合企業(yè)發(fā)展實際,挖掘數(shù)據(jù)驅(qū)動價值增值的關(guān)鍵點,布局未來。 隨著中國能源結(jié)構(gòu)持續(xù)大幅優(yōu)化,清潔低碳化進(jìn)程不斷加快,中國市場蘊含巨大機(jī)遇,國內(nèi)對能源行業(yè)的人才需求...
閱讀 1509·2021-08-09 13:47
閱讀 2769·2019-08-30 15:55
閱讀 3492·2019-08-29 15:42
閱讀 1115·2019-08-29 13:45
閱讀 3009·2019-08-29 12:33
閱讀 1742·2019-08-26 11:58
閱讀 983·2019-08-26 10:19
閱讀 2411·2019-08-23 18:00