28.2.2选择正确的方法
作用域意识
随着作用域链中作用域数量的增加,访问当前作用域外部变量所需的时间也会增加。 访问全局变量始终比访问局部变量慢,因为必须遍历作用域链。任何可以缩短遍历作用域链时间的举措都能提升代码性能。
避免全局查找
全局变量和函数相比于局部值始终是 最费时间的,因为需要经历作用域链查找。来看下面的函数:
function updateUI() {
// 1次
let imgs = document.getElementsByTagName("img");
for (let i = 0, len = imgs.length; i < len; i++) {
//2次
imgs[i].title = "${document.title} image ${i}";
}
// 3次
let msg = document.getElementById("msg");
msg.innerHTML = "Update complete.";
}
这个函数看起来好像没什么问题,但其中三个地方引用了全局 document 对象。如果页面的图片非 常多,那么 for 循环中就需要引用 document 几十甚至上百次,每次都要遍历一次作用域链。
通过在局部作用域中保存 document 对象的引用,能够明显提升这个函数的性能,因为只需要作用域链查找。
通过创建一个指向 document 对象的局部变量,可以通过将全局查找的数量限制为一个来提高这个函数 的性能:
function updateUI() {
// 指向局部变量
let doc = document;
let imgs = doc.getElementsByTagName("img");
for (let i = 0, len = imgs.length; i < len; i++) {
imgs[i].title = "${doc.title} image ${i}";
}
let msg = doc.getElementById("msg");
msg.innerHTML = "Update complete.";
}
这里先把 document 对象保存在局部变量 doc 中。然后用 doc 替代了代码中所有的 document。 这样调用这个函数只会查找一次作用域链,相对上一个版本,肯定会快很多。
因此,一个经验规则就是,只要函数中有引用超过两次的全局对象,就应该把这个对象保存为一个 局部变量。
不使用 with 语句
选择正确的方法
避免不必要的属性查找
| 表示法 | 名称 | 说明 |
| ---- | ---- | ---- |
| O(1) | 常量 | 无论多少值,执行时间都不变。表示简单值和保存在变量中的值 |
| O(logn) | 对数 | 执行时间随着值的增加而增加,但算法完成不需要读取每个值。例子:二分查找 |
| O(n) | 线性 | 执行时间与值的数量直接相关。例子:迭代数组的所有元素 |
| O(n2) | 二次方 | 执行时间随着值的增加而增加,而且每个值至少要读取 n 次。例子:插入排序 |
特别要注意避免通过多次查找获取一个值。例如,看下面的例子:
let query = window.location.href.substring(window.location.href.indexOf("?"));
这里有 6 次属性查找:3 次是为查找 window.location.href.substring()
,3 次是为查找
window.location.href.indexOf()
。通过数代码中出现的点号数量,就可以知道有几次属性查找。
以上代码效率特别低,这是因为使用了两次 window.location.href
,即同样的查找执行了两遍。
只要使用某个 object 属性超过一次,就应该将其保存在局部变量中。第一次仍然要用 O(n)的复杂
度去访问这个属性,但后续每次访问就都是 O(1),这样就是质的提升了。例如,前面的代码可以重写为
如下:
let url = window.location.href;
let query = url.substring(url.indexOf("?"));
这个版本的代码只有 4 次属性查找,比之前节省了约 33%。在大型脚本中如果能这样优化,可能就 会明显改进性能。
通常,只要能够降低算法复杂度,就应该尽量通过在局部变量中保存值来替代属性查找。另外,如 果实现某个需求既可以使用数组的数值索引,又可以使用命名属性(比如 NodeList 对象),那就都应 该使用数值索引。
其他性能优化注意事项
- 原生方法很快。应该尽可能使用原生方法,而不是使用 JavaScript 写的方法。
- switch 语句很快。如果代码中有复杂的 if-else 语句,将其转换成 switch 语句可以变得更 快。然后,通过重新组织分支,把最可能的放前面,不太可能的放后面,可以进一步提升性能。
- 位操作很快