JavaScript 设计模式与开发实践读书笔记

JavaScript 设计模式与开发实践读书笔记

最近利用碎片时间在 Kindle 上面阅读《JavaScript 设计模式与开发实践读书》这本书,刚开始阅读前两章内容,和大家分享下我觉得可以在项目中用的上的一些笔记。

我的 github 项目会不定时更新,有需要的同学可以移步到我的 github 中去查看源码: https://github.com/lichenbuliren/design-mode-notes

1、currying 函数柯里化

currying 又称 部分求值 。一个 currying 的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,将刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性的用于求值。

假设我们需要编写一个计算每个月开销的函数,在每天结束之前,我们要记录每天花掉了多少钱。

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

2、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);

3、函数节流

JavaScript 中的函数大多数情况下都是由用户主动调用触发的,除非是函数本身的实现不合理,否则我们一般不会遇到跟性能相关的问题。但是在一些少数情况下,函数的触发不是有由用户直接控制的。在这些场景下,函数有可能被非常频繁的调用,而造成大的性能问题。

函数被频繁调用的场景:

  • window.onresize 事件
  • 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);

4、分时函数

我们经常会遇到这么一种情况,某些函数确实是用户主动调用的,但是因为一些客观原因,这些函数会严重地影响页面性能。

一个例子就是创建 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);
  }
}

5、惰性加载函数

以创建事件绑定函数为例:

在进入第一个条件分支之后,在函数内部重写这个函数,重写之后,就是我们所需要的函数,在下一次进入的时候,就不再需要判断了。

/**
 * 事件绑定
 * @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

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

时间: 2025-01-21 04:59:28

JavaScript 设计模式与开发实践读书笔记的相关文章

深入Ajax架构和最佳实践读书笔记

在Web开发中,因为Ajax 是前端和后台交互的主要方式之一,其他的交互方式有Flash,Slverlight.特别是网页游戏,大多数还是采用Flash,一方面可以使用 ActionScript来编写代码,虽然ActionScript也是根据ECMAScript规范来实现的,但是它面向对象观点要比 JavaScript好,所以也适合大型游戏和企业级开发.短期内,HTML5+JavaScript想完全代替Flash/Flex技术,还是不可能 的. 这 本书针对时Ajax高级开发,介绍了前端和后台的

编写高效的JAvascript——Nicholas C. Zakas(读书笔记)

Nicholas C. Zakas 在<编写高效的Javascript>一文里总结如下:   管理作用域非常重要,因为存取非局部变量要比局部变量耗时更多.尽量避免使用增长作用域链的结构,比如使用with语句和try-catch从句.如果非局部变量的使用超过一次,那么为了降低性能损耗,就应该将它存储到一个局部变量中.   存储和读取数据的方式对脚本性能影响极大.字面量和局部变量总是最快的:存取数组元素和对象属性会引起性能损耗.如果数组元素或者对象属性的使用超过一次,那么,为了提高存取速度,就应该

《JavaScript DOM 编程艺术》读书笔记之JavaScript 语法_javascript技巧

注释         单行注释://         多行注释:/* */         "<!--"可以用作单行注释,由于和HTML的"<!--  -->"多行注释类似,容易混淆,所以不建议这种注释方法 变量        在JavaScript 语言里,变量和其他语法元素的名字都是区分字母大小写的.名字mood的变量与名字是Mood.MOOD或mOOd的变量没有任何关系,它们不是同一个变量.        JavaScript 语法不允许变量

《JavaScript DOM 编程艺术》读书笔记之JavaScript 简史_javascript技巧

JavaScript 是Netscape公司与Sun公司合作开发的.在 JavaScript 1.0发布时,Netscape Navigator主宰着浏览器市场.微软在推出IE3的时候发布了自己的VBScript语言,同时以JScript为名发布了JavaScript 的一个版本,很快赶上了 Netscape 的步伐.面对微软公司的竞争,Netscape 和 Sun公司联合ECMA(欧洲计算机制造商协会)对JavaScript 语言进行了标准化,于是出现了ECMAScript语言,这是同一种语言

《JavaScript DOM 编程艺术》读书笔记之DOM基础_javascript技巧

DOM              DOM:文档对象模型: 节点        元素节点:DOM的原子是元素节点.<body>.<p>.<ul>之类的元素.元素可以包含其他的元素.没有被包含在其他元素里的唯一元素是<html>元素        文本节点:在XHTML文档里,文本节点总是被包含在元素节点的内部.        属性节点:属性节点用来对元素做出更具体的描述.例如,几乎每个元素都有一个title属性,而我们可以利用这个属性对包含在元素里的东西作出准

设计模式---读书笔记

一.文章来由 按照惯例,来一个来由,这是<设计模式-可复用面向对象软件的基础>的读书笔记,整理给自己看的,整理的内容也会不断更新.大神轻喷~~如果不喜欢请留言说明原因再踩哦,谢谢,我也可以知道原因,不断进步 二.读书笔记 1.P12 可复用的面向对象设计的原则: (1)针对接口编程,而不是针对实现编程.不将变量声明为某个特定的具体类的实例对象,而是让它遵从抽象类所定义的接口: (2)优先使用对象组合,而不是类继承.理想情况下,你不应为获得复用而去创建新的构件.你应该能够只使用对象组合技术,通过

《SQL Server企业级平台管理实践》读书笔记——关于SQL Server数据库的备份方式

原文:<SQL Server企业级平台管理实践>读书笔记--关于SQL Server数据库的备份方式 数据备份一直被认为数据库的生命,也就是一个DBA所要掌握的主要技能之一,本篇就是介绍SQL Server备份原则,SQL Server数据库分为数据文件和日志文件.为了使得数据库能够恢复一致点,备份不仅需要拷贝数据数据文件里的内容,还要拷贝日志文件里的内容.那么根据每次备份的目标不同,我们可以将备份分为数据备份和日志备份. 数据备份的范围可以是完整的数据库.部分数据库.一组文件或文件组.所以根

《SQL Server企业级平台管理实践》读书笔记——几个系统库的备份与恢复

原文:<SQL Server企业级平台管理实践>读书笔记--几个系统库的备份与恢复 master数据库 master作为数据库的主要数据库,记录着SQL Server系统的所有系统级信息,例如登录用户.系统配置设置.端点和凭证以及访问其他数据服务器所需要的信息.master数据库还记录着启动服务器实例所需要的初始化信息,每个其它数据库的主文件位置.master数据库是SQL Server启动的时候打开的第一个数据库.SQL Server是从这个数据库里找到其它数据的信息的.如果master数据

《SQL Server企业级平台管理实践》读书笔记——SQL Server中数据文件空间使用与管理

原文:<SQL Server企业级平台管理实践>读书笔记--SQL Server中数据文件空间使用与管理 1.表和索引存储结构 在SQL Server2005以前,一个表格是以一个B树或者一个堆(heap)存放的.每个B树或者堆,在sysindexes里面都有一条记录相对应.SQL Server2005以后,引入了分区表的概念(Table Partition),在存储组织上,现有的分区基本上替代了原来表格的概念,原先表的概念成为了一个逻辑概念.一个分区就是一个B树或者一个堆.而一张表格则是一个