最近利用碎片時間在 Kindle 上面閱讀《JavaScript 設計模式與開發實踐讀書》這本書,剛開始閱讀前兩章內容,和你們分享下我以爲能夠在項目中用的上的一些筆記。javascript
個人 github 項目會不定時更新,有須要的同窗能夠移步到個人 github 中去查看源碼:https://github.com/lichenbuliren/design-mode-notesjava
currying 又稱部分求值。一個 currying 的函數首先會接受一些參數,接受了這些參數以後,該函數並不會當即求值,而是繼續返回另一個函數,將剛纔傳入的參數在函數造成的閉包中被保存起來。待到函數被真正須要求值的時候,以前傳入的全部參數都會被一次性的用於求值。git
假設咱們須要編寫一個計算每月開銷的函數,在天天結束以前,咱們要記錄天天花掉了多少錢。github
通用 currying 函數:設計模式
var currying = function(fn) { var args = []; return function() { if (arguments.length === 0) { return fn.apply(this, args); } else { [].push.apply(args, arguments); // 返回函數自己,這裏指向 return 後面的匿名函數! return arguments.callee; } } }; var cost = (function() { // 閉包存儲最後的值 var money = 0; return function() { for (var i = 0, len = arguments.length; i < len; i++) { money += arguments[i]; } return money; } })(); // 轉化成 currying 函數 // 這個時候,閉包內部的 fn 指向真正的求值函數 // 也就是 cost 自運行的時候返回的匿名函數 var cost = currying(cost); cost(200); cost(300); cost(500); // 求值輸出 console.log(cost());
Function.prototype.uncurrying = function() { // 此時 selft 是後面例子中的 Array.prototype.push; var self = this; return function() { // arguments: { '0': { '0': 1, length: 1 }, '1': 2 } var obj = Array.prototype.shift.call(arguments); return self.apply(obj, arguments); } }; // 另一種實現方式 Function.prototype.uncurrying = function() { var self = this; return function() { return Function.prototype.call.apply(self, arguments); } }; var push = Array.prototype.push.uncurrying(); var obj = { "length": 1, "0": 1 }; push(obj, 2); console.log(obj);
JavaScript 中的函數大多數狀況下都是由用戶主動調用觸發的,除非是函數自己的實現不合理,不然咱們通常不會遇到跟性能相關的問題。可是在一些少數狀況下,函數的觸發不是有由用戶直接控制的。在這些場景下,函數有可能被很是頻繁的調用,而形成大的性能問題。瀏覽器
函數被頻繁調用的場景:閉包
window.onresize 事件app
mousemove 事件函數
上傳進度性能
上面三個提到的場景,能夠發現它們面臨的共同問題是函數被觸發的頻率過高。
好比咱們在 window.onresize 事件中要打印當前瀏覽器窗口大小,在咱們拖拽改變窗口大小的時候,控制檯1秒鐘進行了 10 次。而咱們實際上只須要 2 次或者 3 次。這就須要咱們按時間段來忽略掉一些事件請求,好比確保在 500ms 內打印一次。很顯然,咱們能夠藉助 setTimeout 來完成。
/** * 函數節流實現 * @param {Function} fn 須要節流執行的函數 * @param {[type]} interval 事件執行間隔時間,單位 ms * @return {[type]} [description] */ var throttle = function(fn, interval) { var _self = fn, timer, firstTime = true; console.log(_self); return function() { var args = arguments, _me = this; // 這裏表明當前的匿名函數 console.log(_me); if (firstTime) { _self.apply(_me, args); return firstTime = false; } if (timer) { return false; } timer = setTimeout(function() { clearTimeout(timer); timer = null; _self.apply(_me, args); }, interval || 500); }; }; window.onresize = throttle(function() { console.log('test'); }, 500);
咱們常常會遇到這麼一種狀況,某些函數確實是用戶主動調用的,可是由於一些客觀緣由,這些函數會嚴重地影響頁面性能。
一個例子就是建立 WebQQ 的 QQ 好友列表。列表中一般會有成百上千個好友,若是一個好友用一個節點來表示,當咱們在頁面中渲染這個列表的時候,可能要一次性往頁面中建立成百上千個節點。
在短期內往頁面中大量添加 DOM 節點顯然也會讓瀏覽器吃不消,咱們看到的結果每每就是瀏覽器的卡頓甚至假死。因此咱們須要一個分時函數來解決這個問題
/** * 分時函數例子 * 以建立 WebQQ 列表爲例 * @param {[type]} data 函數執行須要用到的數據 * @param {Function} fn 真正須要分時執行的函數 * @param {[type]} count 每次建立一批節點的數量 * @param {[type]} interval 函數執行間隔 * @return {[type]} [description] */ var timeChunk = function(data, fn, count, interval) { var t; var len = data.length; var start = function() { for (var i = 0; i < Math.min(count || 1, data.length); i++) { var obj = data.shift(); fn(obj); } } return function() { t = setInterval(function() { if (data.length === 0) { return clearInterval(t); } start(); }, interval); } }
以建立事件綁定函數爲例:
在進入第一個條件分支以後,在函數內部重寫這個函數,重寫以後,就是咱們所須要的函數,在下一次進入的時候,就再也不須要判斷了。
/** * 事件綁定 * @param {[type]} el [description] * @param {[type]} type [description] * @param {[type]} handler [description] */ var addEvent = function(el, type, handler) { if (window.addEventListener) { addEvent = function(el, type, handler) { el.addEventListener(type, handler, false); } } else if (window.attachEvent) { addEvent = function(el, type, handler) { el.attachEvent('on' + type, handler); } } addEvent(el, type, handler); }
暫時這麼多,之後會不按期更新一些關於我讀這本書的筆記內容!