# 编程风格
# null 和 undefined
null == undefined 返回的结果是 true,但两者用途差别很大
undefined 应该避免被使用,它是用于标识一个变量没有被初始化或没有声明
null 主要用于:
- 变量可能被赋值为一个对象时,将其赋值为 null 初始化
- 当函数的参数期望是一个对象时,可传入 nul
- 当函数的返回值期望是一个对象时,可返回 nul
不应当用 null 的场景:
- 检测函数是否传入了某个参数
- 检测一个未初始化的变量
# 变量声明
使用 var 声明变量,或者声明函数时,会被 JavaScript 引擎提升至顶部,导致:
- 在声明变量的语句之前使用这个变量不会报错,且能获取到这个变量的值为 undefined
- 函数可以在声明的代码前面使用
即使是 ES6 的 let、const 也是会有变量提升的,只不过声明前使用会报错,而不是取 undefined 为变量的值
# 严格模式
不要在全局作用域中使用 "use strict";,这会导致文件中的所有代码都以严格模式运行,特别是在将多个文件合并为一个文件时,若其中一个使用了严格模式,会导致其他所有文件也以严格模式运行,甚至可能会导致意料之外的报错
# eval 和 Function
禁止使用 Function 来用字符串创建函数,并且只在别无他法的时候使用 eval
这两者一般用于一些库当中,平常开发最好不要用,同时严格模式下 eval 是被禁用的
# 原始包装类型
原始值类型的变量具有对象行为,可使用 . 符号获取并使用原始包装类型上的属性,这是因为 JavaScript 引擎在使用 . 符号时,创建了一个临时对象,调用完就被销毁了,流程如下:
var name = 'name'
// 此时会创建一个临时的 String 实例
name.toUpperCase()
// 使用完毕后销毁这个示例,在下一次使用时再重新创建一个临时实例
name.tolowerCase()
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 中抽离
主要是避免两种写法:
- 将事件监听直接写在 HTML 中
- 在
<script>
标签中写内联的脚本代码
# 将 HTML 从 JavaScript 中抽离
在 JS 中使用 HTML 通常是给 innerHTML
属性赋值一个 HTML 的字符串,有以下缺点:
- 增加了跟踪文本和结构性问题的复杂度
- 修改文本或标签时,需要到不止一个地方去修改代码
- 字符串需要转义
低耦合的方法:
- 将模板放在服务器上,通过请求获取 HTML 并注入到页面
- 使用模板文件或注释,从中提取出模板文本并生成对应 DOM 节点添加至页面
- 使用诸如
Handlebar
之类的模板引擎,可以实现复杂的模板
# 全局变量
# 全局变量带来的问题
- 容易导致命名冲突
- 代码脆弱,任何对全局变量的修改都可能导致某处报错
- 测试时若依赖了全局变量,会导致难以测试,需要为其创建完整的全局环境
# 避免意外的全局变量
不小心省略了 var
语句(let
、const
同理)可能在不知情的情况下,添加一个新的全局变量或者修改某个已存在的全局变量,通过 Lint 和 Hint 可以避免
# 单全局变量
只创建一个新的全局对象,并将所有功能代码都挂载到这个全局对象上
即使只用一个对象也是需要命名空间的介入。通常会在这个全局对象上创建一个命名空间对象,在这个命名空间里去定义对应的变量
# 零全局变量
使用立即执行的函数调用,并在它的作用域中创建变量,来避免创建全局变量
适用于不被其他地方所依赖的代码
# 事件处理
# 隔离应用逻辑
将注册事件的代码和应用逻辑的代码分开,这样同一段功能代码可以被其他事件触发,而不仅局限于他被用来注册的那一事件
# 不要随意分发事件对象
不用将 event 对象随意分发,应用逻辑不应该依赖于 event 对象,这样不好写测试代码,因为我们没办法清楚的知道这个方法用到了哪些信息,而不得不为测试代码构建整个 event 对象
最好是只分发有需要的数据,如需要知道鼠标点击在哪个坐标来进行特定操作,就只需要将 event.clientX
和 event.clientY
传入方法即可
TIP
这个思想其实不适用于事件对象,任何函数其实都应该遵循这一规则:只传入函数用到的值
这样做不只是让一个函数更加可控、对开发者更加透明,也使得单元测试更好写
# 检测数据类型
# 检测原始值
只需要使用 typeof
即可
# 检测引用值
可以用 instanceof
检测对象是否是某一个构造函数的实例,包括继承了这个类的实例
检测函数最好使用 typeof
,返回的会是 'funciton'
检测数组可以使用 Array.isArray()
方法
一个较为通用的方法:Object.prototype.toString.call(value)
,但不适用于自定义对象