Javascript 优化项目代码技巧之语言基础(二)

博客逐步迁移至 极客兔兔的小站javascript

    上一篇随笔介绍了如何正确判断对象类型、避免变量污染,特殊值(null、undefined、NaN)的使用,以及其余Javascript中经常使用关键字与方法的优化,这篇随笔将着重介绍Javascript语言中的条件与循环优化。
    若有问题,请不吝指出,很是感谢;若是喜欢,右下角点个推荐吧~html

1.if、switch、查表

1.1 if-else分治策略

// 方法一,假设value的值平均分布
// 方法一的平均查询次数是 (n+1)/2,即复杂度是O(N)
// 方法二采用了二分查找,平均查找次数为lg(n),复杂度是对数级别的
if(value === 0){
    func0();
} else if (value === 1){
    func1();
} else if (value === 2){
    func2();
} else if (value === 3){
    func3();
} else if (value === 4){
    func4();
} else if (value === 5){
    func5();
} else if (value === 6){
    func6();
} else if (value === 7){
    func7();
}
// 方法二(分治策略),为方便排版,压缩、省略部分代码块
if (value < 4) {
    if(value < 2){
        if(value === 0){ func0(); }
        else { func1(); }
    }else {
        if(value === 2){ func2(); }
        else { func3(); }
    }
} else {
    // 省略同上的结构...
}
  • 优化技巧一:二分查找可以显著下降复杂度,提升性能,上述例子只是其中一个,在项目中涉及到判断,查找而数据量较大时能够考虑二分提升性能。
  • 优化技巧二:对于if-else语句,判断状况较少,或者考虑代码可读性(好比判断周一到周五),方法一会更直观,不能一味追求效率。
  • 优化技巧三:若是判断值不是平均分布的,那么考虑将出现频率高的判断放在前面(上例中若是90%状况下value等于7,几乎每次都要判断8次,若是将这个判断放在最前面,就降到了1次)。

1.2 什么状况下用switch

  • switch-case语句利用转发表,快速定位入口,在数据量较大时,switch-case执行效率会比if-else好不少。
  • 数据量较多时,switch-case的可读性也会好不少。
  • 如下状况适合用switchjava

    (1) 判断值超过2个,并且值是离散的。 例如 case '男' (2) 表达式的值是固定的,不是动态变化的

    (3) 表达式的值值通常为整数、字符串等类型的数据node

1.3 使用查表提升性能

  • 当有大量离散值须要测试时,若是有一张转发表能够直接查询,那么无疑能够极大地提升性能,若是将这个表以字典(dict)的形式呈现,可读性也获得了提升。Javascript中字典每每等价于对象,举两个例子:
/* 适用状况一:条件执行代码不多,如使用if或switch显得笨重 */
function func1(key){
    if(key === 0){ return ans0; }
    if(key === 1){ return ans1; }
    // 省略 2,3,4,5...
}
// 替换为
function func1(key){
    var ans = [ans0,ans1,ans2,ans3 ... ];
    return ans[key];
}
/* 适用状况二:条件执行代码不少,如用if或switch可读性不好 
 * 同时,执行代码可能屡次复用,并且总体性较强
 * 这种写法经常使用在javasript框架中,书写钩子函数(Hook)
 * 可读性很高,并且容易扩展,代码自己即文档
 */
function func1(key){
    if(key === "created"){  /* ... 省略10行代码 */ }
    else if(key === "init"){ /* ... 省略10行代码 */ }
    // 省略 "resume","start","pause","destroy",...
}
// 替换为
var hooks = {
    "created": function(){ /* ... 省略10行代码 */ };
    "init": function(){ /* ... 省略10行代码 */ };
    // ... 省略更多属性
}
function func1(key){ 
    hooks[key]();
}

2.短路表达式(&&和||)

2.1 改善 && 与 || 性能

  • 使用&&运算时,从左到右计算表达式,一旦为false就返回,例如:数组

    A && B,若是A为false,表达式返回false,再也不计算B。

    A为true,会继续计算B,再决定返回值,这称为短路表达式
    所以,若是B为false的几率大于A,写为B && A能够减小计算次数缓存

  • 使用||运算时,一样先计算||左侧的表达式,例如:框架

    A || B,若是A为true,表达式返回true,再也不计算B。

    所以,若是B为true的几率大于A,写为B || A能够减小计算次数函数

2.2 巧用短路表达式赋值

  • &&|| 不只用在判断语句中,经常使用在赋值语句中
a = {} || [] // => a = {},a并无被赋值为true,而是 {}
  • 利用javascript的弱类型,咱们能够构造很精巧的赋值表达式,0、""、null、false、undefined、NaN 判断为false,其余为true
/* || */
var value = a || [];
// 等价于
value = a ? a : [];
// 等价于
if(a){ value = a; }
else { value = []; } 
 
/* && */
var value = a && b;
// 等价于
value = a ? b : a;
  • 当表达式较简单时,使用三目运算符也能让咱们的代码很优雅,可是当表达式数量不少时,&&和||能更优雅。
/* 当a和b均返回false时,返回一个新建对象 */
return a || b || {};

/* jQuery中大量使用短路表达式赋值 
 * 代码过于简洁,代码可读性会下降,适当使用
 */
diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
        (~b.sourceIndex || MAX_NEGATIVE) -
        (~a.sourceIndex || MAX_NEGATIVE);
  • Javascript的弱类型使得短路表达式赋值很容易,可是有时咱们须要明确返回一个布尔值,这个时候能够强制类型转换。
var value = !!( a || b) // => value = true/false 
if(!!(a && b)) // => 显式转换,与if(a && b)结果一致

3.循环优化

3.1 少用 forEach

  • Javascript的数组有一个原型方法:forEach,能够在每个成员上执行一个函数,forEach接收3个参数,数组项的值(value),数组项的索引(index),以及数组自身(array)
  • forEach 相比通常的循环,将关联额外的函数调用,若是将forEach改成普通的循环结构,效率将大大提升。
items = [1,2,3];
items.forEach(function(value,index,array) {
    // ... func(value);
})
// 等价于
var len = items.length;
for(var i = 0; i < len; ++i ){
    // ... func(items[i]);
}

3.2 重复变量暂存

  • 在循环中,重复使用一个变量的状况是十分常见的,例如上个例子的中len,若是不使用变量len暂存,那么每次循环都须要访问数组的length属性,比起直接访问一个数字,访问一个对象属性的花销会更大。
  • 变量暂存在这个例子中优点并无那么明显,可是若是循环中进行了DOM操做,咱们知道DOM操做是至关费时的,若是将DOM对象缓存,性能提升就很可观了。
// 暂存变量前,获取 p#item,须要3次DOM操做,
$('p#item').func1();  
$('p#item').func2();
$('p#item').func3();
// 等价于(这里不考虑jQuery的链式操做)
// 只须要一次DOM操做(DOM操做很是耗时)
var p_item = $('p#item');
p_item.fun1();
// ...
  • 重复使用的须要计算的变量赋值给另外一个变量,还有一个好处是易于压缩,考虑下面的例子
/* 不暂存变量 */
var obj = { nickname:{} };
obj['nickname'].firstname = 'Tom';
obj['nickname'].lastname = 'Smith';
obj['nickname'].fullname = 'Tom Smith';
// 压缩结果
var a = { nickname:{} };
a['nickname'].firstname = 'Tom';
a['nickname'].lastname = 'Smith';
a['nickname'].fullname = 'Tom Smith';

/* 暂存变量 */
var obj = { nickname:{} };
var nickname = obj['nickname'];
nickname.firstname = 'Tom';
nickname.lastname = 'Smith';
nickname.fullname = 'Tom Smith';
// 压缩结果以下
var a = { nickname:{} };
var b = a['nickname'];
b.firstname = 'Tom';
b.lastname = 'Smith';
b.fullname = 'Tom Smith';
  • 实际项目中,Javasript文件的代码动辄上万行,变量暂存以后,对性能影响以及压缩后节省的空间不可小视,定义变量时,不妨慷慨一点。

分享创造价值,喜欢这个系列文章,不妨关注一下~性能

相关文章
相关标签/搜索