• 高性能JavaScript(编程实践)


    避免双重求值
    JavaScript 允许你在程序中提取一个包含代码的字符串,然后动态执行,有四种方法可以实现,eval(),Function() 构造函数 settimeout 和 setinterval。每个方法都允许你传入一个 JavaScript 代码字符串并执行它

    var num1 =5,
          num2 = 6,
          result = eval("num1+num2"),
          sum = new Function("arg1","arg2","return arg1+arg2");
    
    setTimeout("sum=num1+num2",100);
    setInterval("sum=num1+num2",100);

    以上代码都会导致双重求值的性能消耗,首先是正常的方式求值,然后在执行过程中对于字符串代码发起另一个请求。

    如果使用 eval() 来访问数组条目

    var item = array[0];
    var item = eval("array[0]");

    如图所示,相比于原生代码,使用eval()还是很慢很慢的。 同样的 使用另外三种方法时,也必然使得代码执行的速度变慢

    大多数时候,也很少使用 eval 和 Function。对于 setTimeout和setInterval,传入函数而不是字符串。

    setTimeout(function() {
      alert('hello world');
    }, 1000);

    使用Object/Array直接量

    // 使用直接量
    var obj = {
      name: 'wyh',
      age: 24,
    };
    var arr = ["nicholas", 50, true];
    
    // 不使用直接量
    var obj = new Object();
    obj.name = 'wyh';
    obj.age = 24;
    
    var arr = new Array();
    arr[0] = "nicholas";
    arr[1] = 50;
    arr[2] = true;

    尽管从技术角度上来说,不使用直接量的代码并没有什么问题,但在浏览器中使用直接量的方式会运行的更快。而且还有一个额外的好处就是 节省代码量。

    避免重复工作

    最常见的重复峰做就是浏览器探测,基于浏览器的功能作分支判断导致产生大量代码。

    添加事件处理器的例子:

    function addHandler(target, eventType, handler) { 
      if (target.addEventListener) {  // DOM2 Events
        target.addEventListener(eventType, handler, false);
      } else {  // IE
        target.attachEvent('on' + eventType, handler);
      }
    }   
    
    addHandler(document, 'click', function() {
      console.log('hello world');
    });

    一个页面假如有多次调用 addHandler 函数添加事件,那么每次浏览器都会做判断,来执行哪一个方法。事实上每次的判断结果都是一样的。有几种方法可以避免。


    延迟加载

    延迟加载,也称惰性加载,惰性载入等。延迟加载意味着在信息被使用前不会做任何操作:

    function addHandler(target, eventType, handler) { 
      if (target.addEventListener) {  // DOM2 Events
        addHandler = function(target, eventType, handler) {
          target.addEventListener(eventType, handler, false);
        };
      } else {  // IE
        addHandler = function(target, eventType, handler) {
          target.attachEvent('on' + eventType, handler);
        };
      }
      addHandler(target, eventType, handler);
    }    
    
    addHandler(document, 'click', function() {
      console.log('hello world');
    });
    
    addHandler(window, 'keydown', function() {
      console.log('key down');
    });

    方法在第一个调用的时候,会做一次判断决定使用哪种方法去绑定时间处理器,然后原始addHandler会被新的addHandler函数覆盖,最后一步调用新的函数,并传入原始参数。随后每次调用addHandler()都不会再做检测,因为检测代码已经被新的函数覆盖。

    第一次总会消耗较长的时间,因为需先检测再调用完成任务。之后会变快。

    条件预加载

    条件预加载会在脚本加载期间提前检测,而不会等到函数被调用:

    var addHandler = document.addEventListener ? 
      function(target, eventType, handler) {
        target.addEventListener(eventType, handler, false);
      }:
      function(target, eventType, handler) {
        target.attachEvent('on' + eventType, handler);
      };    
    
    addHandler(document, 'click', function() {
      console.log('hello world');
    });

    这个例子就是先检查 addEventListener()是否存在,然后根据结果指定函数。

    条件预加载确保所有函数调用消耗的时间相同,代价是需要在脚本加载时就检测。适用于一个函数马上就要被用到,并且在整个页面的生命周期中频繁出现的场景。

    使用速度快的部分

    位操作

    如果你不太熟悉数字的二进制表示法,用JavaScript中的 toString() 方法并传图参数2 能很容易地把数字转换为字符串形式的二进制表达式。

    var num1 = 25,
    num2 = 3;

    console.log(num1.toString(2)) // '11001'
    console.log(num2.toString(2)) // '11'

    注意:此表达式忽略了数字高位的零

    JavaScript 有四种位逻辑操作符

    // AND 按位与 两个操作数的对应位都是1时,则在该位返回1
    var r1 = 25 & 3; // 1
    alert(r1.toString(2)); // '1'
    
    // OR 按位或 两个操作数的对应位只要有一个为1时,则在该位返回1
    var r2 = 25 | 3; // 27
    alert(r2.toString(2)); // '11011'
    
    // XOR 按位异或 两个操作数的对应位只有一个为1时,则在该为返回1
    var r3 = 25 ∧ 3; // 26
    alert(r3.toString(2)); // '11010'
    
    // NOT 按位取反,遇0返1 反之亦然
    var r4 = 25 & 3; // -26
    alert(r4.toString(2)); // '-11010'

    利用位操作符提升JavaScript 的速度

    for(var i =0,len=rows.length;i<len;i++){
      if(i%2){
        console.log('aaaa');
      }else{
        console.log('bbbb');
      }
    }

    对2的取模计算,需要这个数除以2然后查看余数,偶数的最低位为0,奇数的最低位为1,可以通过让给定数字与数字1进行按位运算,当此数为偶数那么它和1进行按位与运算的结果为0,如果此数为奇数,那么它和1的结果就是1
    上面的代码可以重写如下:

    for(var i =0,len=rows.length;i<len;i++){
      if(i%1){
        console.log('bbbb');
      }else{
        console.log('aaaa');
      }
    }

    虽然代码改动不大,但按位与版本与原始版本快 50% (取决于浏览器)

    原生方法

    无论你的JavaScript代码如何优化,都永远不会比JavaScript引擎提供的原生方法更快,因为JavaScript原生部分在你写代码前就已经存在在浏览器中了,并且都是用底层语言写的,诸如C++。这意味着这些方法会被编译成机器码,成为浏览器的一部分。

    所以尽量用一些内置的函数或者常量,比如Math对象提供的:

    这里的每个数值都是预先计算好的,只需要使用即可,还有一些处理数学运算的方法

    另外一个例子是选择器API,它允许使用CSS选择器来查找DOM节点。CSS查询被JavaScript原生支持并被JQuery发扬光大。JQuery引擎被广泛认为是最快的CSS查询引擎,但是它仍然比原生方法慢。原生的querySelector()和querySelectorAll()方法完成任务平均所需时间是基于JavaScript的CSS查询的10%。所以当原生方法可用时,尽量使用它们。特别是数学运算和DOM操作,用编译后的代码做更多的事情,你的代码就会越快。

    小结:

    避免使用 eval() 和 Function() 构造器来避免双重求值带来的性能消耗。同样的给 setTimeout 和 setInterval 传递函数而不是字符串

    尽量使用直接量创建对象和数组

    避免做重复的工作,当需要检测浏览器时,可使用延迟加载和条件预加载。

    在进行数学计算时,考虑使用直接操作数字的二进制形式的位运算。

    JavaScript 的原生方法总会比你写的任何代码都要快,尽量使用原生方法

  • 相关阅读:
    9.5---所有字符串的排列组合(CC150)
    9.4---集合子集(CC150)
    9.3---魔术索引(CC150)
    5.3(2)----机器人走方格2(CC150)
    9.2---机器人走方格(CC150)
    9.1---上楼梯(CC150)
    5.3---找最近的两个数(CC150)
    5.8---像素设定(CC150)
    7.4---加法替代运算(CC150)
    4.9---二叉树路径和(CC150)
  • 原文地址:https://www.cnblogs.com/wyhlightstar/p/10255893.html
Copyright © 2020-2023  润新知