避免双重求值
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; //
alert(r1.toString(2)); // '1' // OR 按位或 两个操作数的对应位只要有一个为1时,则在该位返回1
var r2 = 25 | 3; //
alert(r2.toString(2)); // '11011' // XOR 按位异或 两个操作数的对应位只有一个为1时,则在该为返回1
var r3 = 25 ∧ 3; //
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 的原生方法总会比你写的任何代码都要快,尽量使用原生方法

高性能JavaScript(编程实践)的更多相关文章

  1. 高性能JavaScript 编程实践

    前言 最近在翻<高性能JavaScript>这本书(2010年版 丁琛译),感觉可能是因为浏览器引擎的改进或是其他原因,书中有些原本能提高性能的代码在最新的浏览器中已经失效.但是有些章节的 ...

  2. 读高性能JavaScript编程 第二章 让我知道了代码为什么要这样写

    代码为什么要这样写? function initUI(){ var doc = document, bd = doc.body, links = doc.getElementsByTagName_r( ...

  3. 读高性能JavaScript编程学英语 第一章第三页第一段话

    When the browser encounters a <script> tag, as in this HTML page, there is no way of knowing w ...

  4. 读高性能JavaScript编程 第四章 Conditionals

    if else 和 switch    &&    递归 if else 和 switch 一般来说,if-else 适用于判断两个离散的值或者判断几个不同的值域.如果判断多于两个离散 ...

  5. 读高性能JavaScript编程 第三章

    第三章  DOM Scripting  最小化 DOM 访问,在 JavaScript 端做尽可能多的事情. 在反复访问的地方使用局部变量存放 DOM 引用. 小心地处理 HTML 集合,因为他们表现 ...

  6. 读高性能JavaScript编程 第一章

    草草的看完第一章,虽然看的是译文也是感觉涨姿势了, 我来总结一下: 由于 大多数浏览器都是 single process 处理 ui updatas and js execute 于是产生问题: js ...

  7. 读高性能JavaScript编程 第四章 Duff's Device

    又要开始罗里吧嗦的 第四章  Summary 了. 这一次我尽量精简语言. 如果你认为 重复调用一个方法数次有点辣眼睛的话 比如: function test(i){ process(i++); pr ...

  8. 高性能javascript学习总结(2)--DOM编程

    我们知道,对DOM的操作都是非常的耗性能的,那么为什么会耗性能呢?      文档对象模型(DOM)是一个独立于语言的,使用 XML和 HTML 文档操作的应用程序接口(API).在浏览器中,主要与 ...

  9. 高性能javascript学习总结(3)--数据访问

    在 JavaScript 中,数据存储位置可以对代码整体性能产生重要影响.有四种数据访问类型:直接量,变量,数组项,对象成员.         直接量仅仅代表自己,而不存储于特定位置. JavaScr ...

  10. 高性能javascript学习笔记系列(5) -快速响应的用户界面和编程实践

    参考高性能javascript 理解浏览器UI线程  用于执行javascript和更新用户界面的进程通常被称为浏览器UI线程  UI线程的工作机制可以理解为一个简单的队列系统,队列中的任务按顺序执行 ...

随机推荐

  1. 机器学习与Tensorflow(1)——机器学习基本概念、tensorflow实现简单线性回归

    一.机器学习基本概念 1.训练集和测试集 训练集(training set/data)/训练样例(training examples): 用来进行训练,也就是产生模型或者算法的数据集 测试集(test ...

  2. redis缓存存在的隐患及其解决方案

    redis缓存1.缓存穿透 1>.什么是缓存穿透? 业务系统需要查训的数据根本不存在,当业务系统查询时, 首先会在缓存中查训,由于缓存中不存在,然后在往数据 库中查,由于该数据在数据库中也不存在 ...

  3. [Leetcode]303.区域和检索&&304.二维区域和检索

    题目 1.区域和检索: 简单题,前缀和方法 乍一看就觉得应该用前缀和来做,一个数组多次查询. 实现方法: 新建一个private数组prefix_sum[i],用来存储nums前i个数组的和, 需要找 ...

  4. centos系统(ssh)登录缓慢(输入账户密码后需要等待若干时间)

    现象:打开ssh连接工具,输入用户名密码,需要等待约3分钟才能进入系统,进入系统后操作都很流畅,同时top查看系统消耗也很低! 解决:在百度和谷歌搜索大量解决方案,都是如下解决方案: #取消ssh的反 ...

  5. 线程中的定时器Timer类

    Timer 定时器 几分钟之后执行一个任务. 创建了一个定时器相当于开启了一条线程,TimerTask相当于一个线程的任务.内部使用wait/notify机制来实现的. 用法非常的简单  就足以里面的 ...

  6. JavaScript初探四

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  7. mysql 导出 sql的执行结果到 csv文件

    需求: 1. 执行某 SQL 的结果: 2. 将结果导出到 csv文件: 3. 通过命令行执行: mysql ;" | sed 's/\t/","/g;s/^/" ...

  8. JS脚本动态给元素/控件添加事件

    最近突然要用到JS脚本动态给元素添加事件.如TextBox的onclick事件.但有的onclick事件原先已经定义了相应代码!这里又不能替代原有方法,而JS脚本里面有个方法可以给控件在原有事件的基础 ...

  9. notepad++ jstool 插件安装

    notepad++ 格式化显示 网上下载 jstool 插件 放入Notepad++\安装目录的plugins位置下,重启即可使用 插件-->JSTool

  10. JVM学习记录-线程安全与锁优化(二)

    前言 高效并发是程序员们写代码时一直所追求的,HotSpot虚拟机开发团队也为此付出了很多努力,为了在线程之间更高效地共享数据,以及解决竞争问题,HotSpot开发团队做出了各种锁的优化技术常见的有: ...