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

暂时这么多,之后会不按期更新一些关于我读这本书的笔记内容!

相关文章
相关标签/搜索