1. 項目中在綁定事件的時候總想在觸發前,或者觸發後作一些統一的判斷或邏輯,在c#後端代碼裏,能夠用Attribute, filter等標籤特性實現AOP的效果,但是js中沒有這種用法,歸根到本質仍是不支持類型的攔截和判斷,因此無法實現,可是js的靈活就在於能夠經過原型鏈, 高階函數,閉包等特性來實現相似的效果,這裏記錄一下便於複習c#
//AOP: after Function.prototype.after = function (afterFn) { var _self = this; return function () { var ret = _self.apply(this, arguments); if (ret === false) { return false; } afterFn.apply(this, arguments); return ret; } } //AOP: before Function.prototype.before = function (beforeFn) { var _self = this; return function () { var ret = beforeFn.apply(this, arguments); if (ret === false) { return false; } return _self.apply(this, arguments); } }
2. 用法下面的例子是工程中實用, 在tab點擊的時候,通常狀況沒打開一個tab要綁定一個或多個事件作點擊,關閉等處理,這裏實現的方法就是在父元素統一隻綁定一個事件,都經過冒泡來觸發,這種好處就是新加tab不用綁定事件,很差的就是可能tab層級複雜,須要作不少元素的判斷來決定觸發什麼事件,好比說下面的關閉事件裏面先判斷後,要是沒有觸發,在判斷觸發點擊事件,這樣寫的邏輯清晰,功能模塊能夠分開,利於解耦,效果也能很好實現!!!後端
_bindEvents: function () { var g = this, p = this.options, container = g.tab.bar.container; // 標籤點擊事件, 統一隻綁定父元素一次 container.nav.bind("click.tab.nav", function (e) { if (e.target === e.currentTarget) { e.stopPropagation(); return; } g._tabCloseClick.after(g._tabBarClick).call(g, e); }); }
3. 固然還有函數的防抖和節流的實現:閉包
函數防抖就是爲了讓一個函數在必定的時間只執行一次,即便被屢次觸發,典型應用就是tab標籤打開不少的時候,不停的拖動窗體大小或者改變位置,這樣標籤會有一些動畫效果用來自適應窗體大小,可是要是頻發的拖動窗體,這些動畫效果就會被觸發屢次,致使拖完了後一段時間內,這種效果不停在重複,延遲嚴重,因此用debounce就能夠在必定時間後觸發一次效果就能夠了,性能提高的同時,體驗也很好app
函數節流 就是規定一個單位時間,在這個單位時間內,只能有一次觸發事件的回調函數執行,若是在同一個單位時間內某事件被觸發屢次,只有一次能生效,在項目中應用的例子就是在手風琴點擊展開層級的時候,若是用戶在短期內重複快速點擊屢次,這樣這個層級就會不停的觸發屢次,展開收起又展開,感受發瘋同樣,因此這時候用throttle節流一下, 好比在300ms內就觸發一次,無論你點了多少次,這樣體驗大大提升函數
/** * 函數防抖, 在事件被觸發n秒後再執行回調,若是在這n秒內又被觸發,則從新計時 * @param fn {Function} 實際要執行的函數 * @param delay {Number} 延遲時間,也就是閾值,單位是毫秒(ms) * @return {Function} 返回一個防抖了的函數 */ vango.utils.debounce = function (fn, delay) { var timer; return function () { var context = this; var args = arguments; clearTimeout(timer); timer = setTimeout(function () { fn.apply(context, args); }, delay); } } /** * 函數節流,規定一個單位時間,在這個單位時間內,只能有一次觸發事件的回調函數執行,若是在同一個單位時間內某事件被觸發屢次,只有一次能生效 * @param fn {Function} 實際要執行的函數 * @param wait {Number} 延遲時間,也就是閾值,單位是毫秒(ms) * @return {Function} 返回一個節流了的函數 */ vango.utils.throttle = function (func, wait, options) { /* options的默認值 * 表示首次調用返回值方法時,會立刻調用func;不然僅會記錄當前時刻,當第二次調用的時間間隔超過wait時,才調用func。 * options.leading = true; * 表示當調用方法時,未到達wait指定的時間間隔,則啓動計時器延遲調用func函數,若後續在既未達到wait指定的時間間隔和func函數又未被調用的狀況下調用返回值方法,則被調用請求將被丟棄。 * options.trailing = true; * 注意:當options.trailing = false時,效果與上面的簡單實現效果相同 */ var context, args, result; var timeout = null; var previous = 0; if (!options) options = {}; var later = function () { previous = options.leading === false ? 0 : new Date();; timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function () { var now = new Date();; if (!previous && options.leading === false) previous = now; // 計算剩餘時間 var remaining = wait - (now - previous); context = this; args = arguments; // 當到達wait指定的時間間隔,則調用func函數 // 精彩之處:按理來講remaining <= 0已經足夠證實已經到達wait的時間間隔,但這裏還考慮到假如客戶端修改了系統時間則立刻執行func函數。 if (remaining <= 0 || remaining > wait) { // 因爲setTimeout存在最小時間精度問題,所以會存在到達wait的時間間隔,但以前設置的setTimeout操做還沒被執行,所以爲保險起見,這裏先清理setTimeout操做 if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { // options.trailing=true時,延時執行func函數 timeout = setTimeout(later, remaining); } return result; }; };
5. 對於一個系統來講事件的設計固然必不可少,平臺中簡單的實現了一下事件總線的機制,記錄一下性能
vango.event = (function () { var _callbacks = {}; var on = function (eventName, callback) { if (!_callbacks[eventName]) { _callbacks[eventName] = []; } _callbacks[eventName].push(callback); }; var off = function (eventName, callback) { var callbacks = _callbacks[eventName]; if (!callbacks) { return; } var index = -1; for (var i = 0; i < callbacks.length; i++) { if (callbacks[i] === callback) { index = i; break; } } if (index < 0) { return; } _callbacks[eventName].splice(index, 1); }; var trigger = function (eventName) { var callbacks = _callbacks[eventName]; if (!callbacks || !callbacks.length) { return; } var args = Array.prototype.slice.call(arguments, 1); for (var i = 0; i < callbacks.length; i++) { callbacks[i].apply(this, args); } }; return { on: on, off: off, trigger: trigger }; })();