《JavaScript 代码优化指南》
Posted 九门提督琪琪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《JavaScript 代码优化指南》相关的知识,希望对你有一定的参考价值。
1. 将脚本放在页面的底部
1
2
3
4
5
|
2. 变量声明合并
将多条var语句合并为一条语句,我建议将未赋值的变量放在最后面。
并且为了代码的美观,还可以将等号对齐。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//糟糕 var oBtn = document.getElementById( \'button\' ); var name = \'\' ; var index; var oLis = document.getElementsByTagName( \'li\' ); var result = [1,2,3,4]; var i; //建议 var oLis = document.getElementsByTagName( \'li\' ), oBtn = document.getElementById( \'button\' ), result = [1,2,3,4], name = \'\' , index, j; |
3. 减少全局变量
减少全局变量,并不是说不定义全局变量,而是我们可以定义一个对象,来保存我们定义的全局变量。
我称这个对象为变量空间。
1
2
3
4
5
6
7
8
9
10
11
12
|
//不推荐 var global = \'This is global Variant\' , config = {}, data = []; // 建议: var varSpace = { global: \'This is global Variant\' , config:{}, data:[] }; |
4. 字面量方式
声明枚举类型的变量,应该采用字面量方式。
1
2
3
4
5
6
7
8
|
//糟糕的 var object = new Object(), array = new Array(), pattrn = new RegExp(); //建议: var object = {}, array = [], pattrn = //; |
5. 缓存DOM
DOM 遍历是非常昂贵的,所以要尽量将会重用的DOM保存起来。
1
2
3
4
5
6
7
8
|
// 糟糕 var height = document.getElementById( \'box\' ).offsetHeight; document.getElementById( \'box\' ).style.background= "#f00" ; // 建议 var oBox = document.getElementById( \'box\' ), height = oBox.offsetHeight; oBox.style.background= "#f00" ; |
6. 使用缓存的父元素来查询子元素
正如前面我们所说的DOM的操作与获取是非常昂贵的,所以对于获取一个DOM元素,首先我们应该检查它有没有已经被缓存了的父元素。如果存在的话,可以直接在父元素的基础上向内去查找。
1
2
3
4
5
6
7
|
// 糟糕的 var oBox = document.getElementById( \'box\' ), oLis = document.getElementsByTagName( \'li\' ); // 建议 var oBox = document.getElementById( \'box\' ), oLis = oBox.getElementsByTagName( \'li\' ); |
7. 条件分支优化
7.1 选择接近
将条件分支,按可能性的顺序从高到低排列,可以减少javascript 解释器对条件语句的探测次数。
例如预计一个数在0~99的范围概率是50%,预计100 - 199的范围时 15% 在 200 - 300 的范围时 30%,最后小于0的概率的是5%。
使用分支语句则可以这样组织代码:
1
2
3
4
5
6
7
8
9
|
if (num>=0 && num<100){ //... } else if (num >=200 && num <= 300){ //... } else if (num >=100 && num < 200){ //... } else { // 小于0 } |
实际上,还可以进一步优化:
1
2
3
4
5
6
7
8
9
10
11
|
if (num >=0){ if (num < 100){ //... } else if (num >= 200 && num <=300){ //... } else if (num >=100 && num < 200){ //... } } else { // 小于0 } |
7.2 缩短检测条件
对检测的条件进行优化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
· 字符串为空或非空 //不推荐: if (name === \'\' ) { // ...... } if (name !== \'\' ) { // ...... } //推荐: if (!name) { // ...... } if (name) { // ...... } · 数组非空 //不推荐: if (collection.length > 0) { // ...... } //推荐: if (collection.length) { // ...... } · 布尔不成立 //不推荐: if (notTrue === false ) { // ...... } //推荐: if (!notTrue) { // ...... } · null 或 undefined //不推荐: if (noValue === null || typeof noValue === \'undefined\' ) { // ...... } //推荐: if (noValue == null ) { // ...... } |
7.3 三目运算符
使用三目运算符可以代替简单的双分支结构。
例如:
1
2
3
4
5
|
if (a > b){ num = a } else { num = b } |
用三目运算符: num = a > b ? a : b;
这里我推荐简单的条件判断,也就是在条件的语句组中,语句并不多,例如下面这种情况,我就不建议使用三目运算符。
1
2
3
4
5
6
|
if (a > b){ num = a; array.push(num); string = array.join( \'\' ); ... } |
7.4 当分支数量大于等于3时,采用switch语句
测试表明,当分支数量大于3时,switch 的分支执行效率要高与if分支。在IE中尤为明显,其性能可以提升50%左右。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
// 糟糕 ... if (num == a){ } else if (num == b){ } else if (num == c){ } else { } // 推荐: switch (num){ case a: ... break ; case b: ... break ; case c: ... break ; default : .... } |
8. 循环优化
8.1 不在循环中声明变量。
在循环中去声明变量或者是定义函数,会严重的消耗性能.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//糟糕: for ( var i=0,l=data.length;i<l;i++){ if (data[i] > 10){ var value = data[i] } } //建议: var value ; for ( var i=0,i<data.length;i++){ if (data[i] > 10){ value = data[i] } } |
8.2 优化循环终止条件
由于每次循环过程都会去计算终止条件,所以可以用一个变量来保存这个终止条件,减少重复性的计算,提高执行速度。
1
2
3
4
5
|
//糟糕的: for ( var i=0;i<data.length;i++){ //...} //建议: for ( var i=0,l=data.length;i<l;i++){ //...} |
8.3 优化循环体
循环体是执行最多的,所以要确保其最大程度的优化。
8.4 for in 循环的优势
for in 循环相比于普通的循环,它更加适合用于遍历对象,这是因为for in 循环不仅可以返回对象的 value值,还可以返回对象当前的key。
示例:
1
2
3
4
5
6
|
for ( key in Object){ if (Object.hasOwnProperty(key)){ //... } } // 注: 参考《JavaScript 语言精粹》 |
9. 巧用 || 和 && 布尔运算符
· 短路求值
1
2
3
4
5
6
7
8
|
//糟糕的: if (!fn){ callback = function (){} } else { callback = fn; } //建议: callback = fn || function (){}; |
· 短路检测执行
1
2
3
4
5
6
|
//糟糕的: if (name){ welcome(); } //建议: name && welcome(); |
10. 符串优化
· 定界符
统一使用单引号(‘),不使用双引号(“)。这在创建 HTML 字符串非常有好处:
示例:
1
|
var msg = \'This is some HTML <div class="makes-sense"></div>\' ; |
· 字符串拼接
对于大量的字符串拼接的情况下,不建议使用 += 进行拼接,而是建议先定义一个数组,然后不断的push,最后再使用join(\'\')进行字符串的拼接
· 换一种角度来看待字符拼接
当你遍历一个数组或对象时,不要总想着使用for语句,要有创造性,总能找到更好的办法,例如向下面这样。
1
2
|
var arry = [ \'item1\' , \'item2\' , \'item3\' , \'item4\' ], string = \'<ul><li>\' + arry.join( \'</li><li>\' )+ \'</li></ul>\' ; |
· String的隐式转换
当有调用String对象的方法或属性时,JavaScript会进行一个隐式装箱操作,将字符串先转换成一个String对象。再去调用对应的方法。
例如:
\'hellow\'.length 实际上进行了 new String(\'hellow\') -> new String(\'hellow\').length
所以,对于会用到String对象的方法或属性的字符串,我推荐通过 new String() 的方式来声明定义。
11. 数组相关
· 获取数组最后一个元素
1
2
3
|
var array = [1,2,3], lastValue = array[array.length-1]; lastValue = array.slice(-1); |
· 数组截断
1
2
|
var array = [1,2,3]; array.length = 0; |
· 数组塞值
对于按顺序增加数组元素,可以在循环中通过push依次添加。
示例:
1
2
3
4
5
6
7
|
var array = []; for ( var i=0,l=data.length;i<l;i++){ // 糟糕 array[i] = i; // 推荐 array.push(i); } |
12. 函数相关
12.1 作用域提升
在 JavaScript 中变量和方法定义会自动提升到执行之前。JavaScript 只有 function 级的定义域,而无其他很多编程语言中的块定义域,所以使得你在某一 function 内的某语句和循环体中定义了一个变量,此变量可作用于整个 function 内,而不仅仅是在此语句或循环体中,因为它们的声明被 JavaScript 自动提升了。我们通过例子来看清楚这到底是怎么一回事:
原 function
1
2
3
4
5
6
7
|
myname = "global" ; function sample() { alert(myname); // "undefined" var myname = "local" ; alert(myname); // "local" } sample() |
被 JS 提升过后
1
2
3
4
5
6
7
8
|
myname = "global" ; function sample() { var myname; //没有赋值 alert(myname); // "undefined" myname = "local" ; //此处赋值 alert(myname); // "local" } sample(); |
* 需要注意的是,虽然作用域内的变量被自动提升到最前进行声明,但是变量赋值的依然是代码中的位置。
正如你所看到的这段令人充满困惑与误解的代码导致了出人意料的结果。只有良好的声明习惯,也就是下一章节我们要提到的声明规则,才能尽可能的避免这类错误风险。
声明提前
为避免上述的变量和方法定义被自动提升造成误解,把风险降到最低,我们应该手动地显示地去声明变量与方法。也就是说,所有的变量以及方法,应当定义在 function 内的首行。
1
2
3
4
5
6
7
8
9
10
11
12
以上是关于《JavaScript 代码优化指南》的主要内容,如果未能解决你的问题,请参考以下文章
Vue3官网-高级指南(十七)响应式计算`computed`和侦听`watchEffect`(onTrackonTriggeronInvalidate副作用的刷新时机`watch` pre)(代码片段 |