《编写可维护的JavaScript》阅读笔记

# 编程风格

# null 和 undefined

null == undefined 返回的结果是 true,但两者用途差别很大

undefined 应该避免被使用,它是用于标识一个变量没有被初始化或没有声明

null 主要用于:

  • 变量可能被赋值为一个对象时,将其赋值为 null 初始化
  • 当函数的参数期望是一个对象时,可传入 nul
  • 当函数的返回值期望是一个对象时,可返回 nul

不应当用 null 的场景:

  • 检测函数是否传入了某个参数
  • 检测一个未初始化的变量

# 变量声明

使用 var 声明变量,或者声明函数时,会被 JavaScript 引擎提升至顶部,导致:

  1. 在声明变量的语句之前使用这个变量不会报错,且能获取到这个变量的值为 undefined
  2. 函数可以在声明的代码前面使用

即使是 ES6 的 let、const 也是会有变量提升的,只不过声明前使用会报错,而不是取 undefined 为变量的值

# 严格模式

不要在全局作用域中使用 "use strict";,这会导致文件中的所有代码都以严格模式运行,特别是在将多个文件合并为一个文件时,若其中一个使用了严格模式,会导致其他所有文件也以严格模式运行,甚至可能会导致意料之外的报错

# eval 和 Function

禁止使用 Function 来用字符串创建函数,并且只在别无他法的时候使用 eval

这两者一般用于一些库当中,平常开发最好不要用,同时严格模式下 eval 是被禁用的

# 原始包装类型

原始值类型的变量具有对象行为,可使用 . 符号获取并使用原始包装类型上的属性,这是因为 JavaScript 引擎在使用 . 符号时,创建了一个临时对象,调用完就被销毁了,流程如下:

var name = 'name'
// 此时会创建一个临时的 String 实例
name.toUpperCase()
// 使用完毕后销毁这个示例,在下一次使用时再重新创建一个临时实例
name.tolowerCase()
1
2
3
4
5

# 编程实践

# UI 层的松耦合

# 将 JavaScript 从 CSS 中抽离

很早以前的浏览器支持在 CSS 中使用 expression 执行 js 代码,会导致错误很难定位,应该避免这样的行为(了解即可,现在没有这用行为了)

# 将 CSS 从 JavaScript 中抽离

应该避免在 js 中通过直接修改 DOM 的 style 属性来操作样式,最佳方法是:

样式信息都写在 CSS 文件里,js 只是操作 CSS 的 className,如添加、修改、删除 className,让 className 成为 CSS 和 JavaScript 之间通信的桥梁,这样 CSS 中的样式代码可以随时修改而不需要改动 js 代码

# 将 JavaScript 从 HTML 中抽离

主要是避免两种写法:

  1. 将事件监听直接写在 HTML 中
  2. <script> 标签中写内联的脚本代码

# 将 HTML 从 JavaScript 中抽离

在 JS 中使用 HTML 通常是给 innerHTML 属性赋值一个 HTML 的字符串,有以下缺点:

  • 增加了跟踪文本和结构性问题的复杂度
  • 修改文本或标签时,需要到不止一个地方去修改代码
  • 字符串需要转义

低耦合的方法:

  1. 将模板放在服务器上,通过请求获取 HTML 并注入到页面
  2. 使用模板文件或注释,从中提取出模板文本并生成对应 DOM 节点添加至页面
  3. 使用诸如 Handlebar 之类的模板引擎,可以实现复杂的模板

# 全局变量

# 全局变量带来的问题

  1. 容易导致命名冲突
  2. 代码脆弱,任何对全局变量的修改都可能导致某处报错
  3. 测试时若依赖了全局变量,会导致难以测试,需要为其创建完整的全局环境

# 避免意外的全局变量

不小心省略了 var 语句(letconst 同理)可能在不知情的情况下,添加一个新的全局变量或者修改某个已存在的全局变量,通过 Lint 和 Hint 可以避免

# 单全局变量

只创建一个新的全局对象,并将所有功能代码都挂载到这个全局对象上

即使只用一个对象也是需要命名空间的介入。通常会在这个全局对象上创建一个命名空间对象,在这个命名空间里去定义对应的变量

# 零全局变量

使用立即执行的函数调用,并在它的作用域中创建变量,来避免创建全局变量

适用于不被其他地方所依赖的代码

# 事件处理

# 隔离应用逻辑

将注册事件的代码和应用逻辑的代码分开,这样同一段功能代码可以被其他事件触发,而不仅局限于他被用来注册的那一事件

# 不要随意分发事件对象

不用将 event 对象随意分发,应用逻辑不应该依赖于 event 对象,这样不好写测试代码,因为我们没办法清楚的知道这个方法用到了哪些信息,而不得不为测试代码构建整个 event 对象

最好是只分发有需要的数据,如需要知道鼠标点击在哪个坐标来进行特定操作,就只需要将 event.clientXevent.clientY 传入方法即可

TIP

这个思想其实不适用于事件对象,任何函数其实都应该遵循这一规则:只传入函数用到的值

这样做不只是让一个函数更加可控、对开发者更加透明,也使得单元测试更好写

# 检测数据类型

# 检测原始值

只需要使用 typeof 即可

# 检测引用值

可以用 instanceof 检测对象是否是某一个构造函数的实例,包括继承了这个类的实例

检测函数最好使用 typeof ,返回的会是 'funciton'

检测数组可以使用 Array.isArray() 方法

一个较为通用的方法:Object.prototype.toString.call(value) ,但不适用于自定义对象

  • 本文作者: Vivek
  • 本文链接:
  • 版权声明:
    本博客所有文章除特别声明外,均默认采用CC BY-NC-SA 4.0许可协议
Loading