JavaScript 設計模式與開發實踐讀書筆記

JavaScript 設計模式與開發實踐讀書筆記

最近利用碎片時間在 Kindle 上面閱讀《JavaScript 設計模式與開發實踐讀書》這本書,剛開始閱讀前兩章內容,和你們分享下我以爲能夠在項目中用的上的一些筆記。javascript

個人 github 項目會不定時更新,有須要的同窗能夠移步到個人 github 中去查看源碼:https://github.com/lichenbuliren/design-mode-notesjava

一、currying 函數柯里化

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());

二、uncurrying 函數

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);
}

Q&A

暫時這麼多,之後會不按期更新一些關於我讀這本書的筆記內容!

相關文章
相關標籤/搜索