zoukankan      html  css  js  c++  java
  • Javascript 优化项目代码技巧之语言基础(二)

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

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

    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的可读性也会好很多。
    • 以下情况适合用switch

    (1) 判断值超过2个,而且值是离散的。 例如 case '男'
    (2) 表达式的值是固定的,不是动态变化的
    (3) 表达式的值值一般为整数、字符串等类型的数据

    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文件的代码动辄上万行,变量暂存之后,对性能影响以及压缩后节省的空间不可小视,定义变量时,不妨慷慨一点。

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

  • 相关阅读:
    程序员修炼之道阅读笔记
    11.5
    11.3
    11.2
    11.1java读取Excel表格
    软工概论第二周学习进度表
    软工概论第二周个人项目四则运算一
    软工概论第一次课堂测试
    软工概论第一周学习进度表
    软工概论第一周动手动脑
  • 原文地址:https://www.cnblogs.com/gzdaijie/p/5338494.html
Copyright © 2011-2022 走看看