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

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

目录
相关文章
|
1月前
|
Web App开发 JavaScript 前端开发
Node.js 是一种基于 Chrome V8 引擎的后端开发技术,以其高效、灵活著称。本文将介绍 Node.js 的基础概念
Node.js 是一种基于 Chrome V8 引擎的后端开发技术,以其高效、灵活著称。本文将介绍 Node.js 的基础概念,包括事件驱动、单线程模型和模块系统;探讨其安装配置、核心模块使用、实战应用如搭建 Web 服务器、文件操作及实时通信;分析项目结构与开发流程,讨论其优势与挑战,并通过案例展示 Node.js 在实际项目中的应用,旨在帮助开发者更好地掌握这一强大工具。
46 1
|
18天前
|
存储 网络架构
Next.js 实战 (四):i18n 国际化的最优方案实践
这篇文章介绍了Next.js国际化方案,作者对比了网上常见的方案并提出了自己的需求:不破坏应用程序的目录结构和路由。文章推荐使用next-intl库来实现国际化,并提供了详细的安装步骤和代码示例。作者实现了国际化切换时不改变路由,并把当前语言的key存储到浏览器cookie中,使得刷新浏览器后语言不会失效。最后,文章总结了这种国际化方案的优势,并提供Github仓库链接供读者参考。
|
25天前
|
Web App开发 JavaScript 前端开发
Node.js开发
Node.js开发
41 13
|
29天前
|
设计模式 算法 开发者
探索编程语言中的设计模式:从理论到实践
设计模式,这一编程世界中的灯塔,为无数开发者照亮了复杂问题解决的道路。本文将深入探讨设计模式在编程实践中的运用,以代码示例揭示其背后的智慧。无论你是初学者还是资深开发者,都能在这里找到启发和共鸣。让我们一起领略设计模式的魅力,开启编程世界的新篇章!
|
1月前
|
存储 JavaScript 前端开发
深入浅出Node.js后端开发
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将以Node.js为例,深入探讨其背后的哲学思想、核心特性以及在实际项目中的应用,旨在为读者揭示Node.js如何优雅地处理高并发请求,并通过实践案例加深理解。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和思考。
|
29天前
|
Web App开发 开发框架 JavaScript
深入浅出Node.js后端开发
本文将带你领略Node.js的魅力,从基础概念到实践应用,一步步深入理解并掌握Node.js在后端开发中的运用。我们将通过实例学习如何搭建一个基本的Web服务,探讨Node.js的事件驱动和非阻塞I/O模型,以及如何利用其强大的生态系统进行高效的后端开发。无论你是前端开发者还是后端新手,这篇文章都会为你打开一扇通往全栈开发的大门。
|
1月前
|
Web App开发 开发框架 JavaScript
深入浅出Node.js后端开发
在这篇文章中,我们将一起探索Node.js的奇妙世界。无论你是刚接触后端开发的新手,还是希望深化理解的老手,这篇文章都适合你。我们将从基础概念开始,逐步深入到实际应用,最后通过一个代码示例来巩固所学知识。让我们一起开启这段旅程吧!
|
28天前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
本文将带领读者从零基础开始,一步步深入到Node.js后端开发的精髓。我们将通过通俗易懂的语言和实际代码示例,探索Node.js的强大功能及其在现代Web开发中的应用。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的见解和技巧,让你的后端开发技能更上一层楼。
|
1月前
|
JavaScript 前端开发 API
深入理解Node.js事件循环及其在后端开发中的应用
本文旨在揭示Node.js的核心特性之一——事件循环,并探讨其对后端开发实践的深远影响。通过剖析事件循环的工作原理和关键组件,我们不仅能够更好地理解Node.js的非阻塞I/O模型,还能学会如何优化我们的后端应用以提高性能和响应能力。文章将结合实例分析事件循环在处理大量并发请求时的优势,以及如何避免常见的编程陷阱,从而为读者提供从理论到实践的全面指导。
|
1月前
|
Web App开发 JavaScript 前端开发
深入浅出Node.js后端开发
本文将带你走进Node.js的世界,从基础到进阶,逐步解析Node.js在后端开发中的应用。我们将通过实例来理解Node.js的异步特性、事件驱动模型以及如何利用它处理高并发请求。此外,文章还会介绍如何搭建一个基本的Node.js服务器,并探讨如何利用现代前端框架与Node.js进行交互,实现全栈式开发。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和深入的理解。
27 4