使用常量:
当出现以下几种情况的时候,就可以将值提取出来作为常量:
重复值:任何在多处用到的值都应抽取为一个常量。
用户界面字符串:任何用于显示给用户的字符串都应抽取为一个常量。
URLs:在web应用中,资源位置很容易变更。
任意可能会更改的值:每当你在用到字面量值的时候,你都要问一下自己这个值在未来是不是会变化。字面量就是赋值给变量的右边的值。
性能:
注意作用域:
随着作用域链中作用域的增加,访问当前作用域以外的变量的时间也在增加。访问全局变量总是要比访问局部变量慢,因为需要遍历作用域。
解决方案:将在一个函数中,会用到多次的全局对象存储为局部变量。
对象属性的访问:
对象属性的访问是一个o(n)操作,对象的任何属性查找都要比访问变量或者数组花费更长时间,因为必须在原型链中对拥有该名称的属性进行一次搜索。简而言之,属性查找越多,执行时间就越长。
解决方案:举个例子:
const query = window.location.href.substring(window.location.href.indexOf(‘?’))
这段代码由于两次用到了window.location.href,同样的查找进行了两次,因此效率特别不好。一旦多次用到对象属性,应该将其存储在局部变量中。第一次的访问该值会是o(n),然后后续的访问都会是o(1),就会节省很多。例如,之前的代码可以如下重写:
const url = window.location.href;
const query = url.substring(url.indexOf(‘?’));
优化循环:
简化终止条件:由于每次循环过程都会计算终止条件,所以必须保证它尽可能快。也就是说避免属性查找或其它O(n)的操作。
简化循环体:循环体是执行最多的,所以要确保其被最大限度的优化。确保没有某些可以被很容易移出循环的密集计算。
展开循环:
当循环的次数是确定的,消除循环并使用多次函数调用往往更快。如果循环中的迭代次数不能事先确定,那可以考虑使用一种叫做duff装置的技术。针对大数据集使用展开循环可以节省很多时间。但对于小数据集,额外的开销则可能得不偿失,以下是例子:
function duff(arr, process) {
const len = arr.leng
const iterations = Math.floor(len/8);
const leftover = len%8;
let i = 0;
if (leftover > 0) {
do {
process(arr[i++]);
} while (--leftover > 0);
}
do {
process(arr[i++]);
process(arr[i++]);
process(arr[i++]);
process(arr[i++]);
process(arr[i++]);
process(arr[i++]);
process(arr[i++]);
process(arr[i++]);
} while(--iterations > 0)
}
性能的其它注意事项:
原生方法较快:只要有可能,使用原声方法而不是自己用JavaScript重新一个。原生方法是用诸如C/C++之类的编译型语言写出来的,所以要不JavaScript的快很多很多。
Switch语句较快:如果有一系列if-else可以考虑换成单个Switch则可以得到更快的代码。还可以通过将case语句按照最可能的到最不可能的进一步优化。
位运算符较快:当进行算数运算的时候,位运算操作要比任何布尔运算或者算数运算快。选择性的用位运算替换算数运算可以极大提升复杂计算的性能。诸如取模,逻辑与,逻辑或都可以考虑用位运算来替换。
最小语句数:
JavaScript代码中的语句数量也影响所执行的操作的速度。
多个变量声明:多个变量声明应该尽量合并成一个单个语句声明,例如:
const a=1,
b=2,
c=3;
插入迭代值:当使用迭代值的时候,尽可能合并语句,例如:
const name = arr[i]
i++;
可以合并成:
const name = arr[i++];
使用数组和字面量:当创建数组和对象时,应该尽量使用字面量而不是使用构造函数,因为这样可以消除不必要的语句:
const a = {
name: ‘a’
}
优化DOM交互:
最小化现场更新:对页面已存在的dom进行的操作,不管是插入还是删除,都是一个现场更新,对性能的损耗非常大。现场更新的越多,代码完成执行所花的时间就越长。完成一个操作所需的现场更新越少,代码就越快。
一种方法是将某一部分整个做移除,然后更新,更新完以后再插入回页面。这样子会造成每次的更新页面会有不必要的闪烁。
最佳方案是使用document.createDocumentFragment(),然后将需要加入的新元素用这个返回对象的appendChild()将所有需要添加到页面的元素添加进去。最后再将对象使用appendChild()添加到页面中。
使用innerHTML:innerHTML也比createElement()和appendChild()之类的方法快很多。同样,一次性调用innerHTML也比多次调用快很多,因为一次innerHTML就是一次现场更新。
使用事件代理:大多数Web应用在用户交互上大量用到了事件处理程序。页面上的事件处理程序的数量和页面响应用户交互的速度之间有个负相关。为了减轻这种损耗,最好使用事件代理。也就是通过冒泡的原理,将多个元素的事件绑定到上层元素当中。
注意HTMLCollection:使用它对Web应用的性能而言是巨大的损害。任何时候访问HTMLCollection,不管它是一个属性还是一个方法,都是在文档上进行一个查询,这个查询开销很昂贵。比如可以将一个HTMLCollection对象的循环,将它的length保存一个变量。以下几种情况会返回HTMLCollection对象:
进行了对getElementsByTagName()的调用。
获取了元素的childNodes属性。
获取了元素的attributes属性。
访问了特殊的集合,如document.forms,document.images等。
网友评论