高性能JavaScript 编程实践
前言
最近在翻《高性能JavaScript》这本书(2010年版 丁琛译),感觉可能是因为浏览器引擎的改进或是其他原因,书中有些原本能提高性能的代码在最新的浏览器中已经失效。但是有些章节的有些内容还是相当不错的,譬如第八章编程实践,为了方便以后的查阅,对此做个总结。失效的代码也会在以后做更进一步的探索。
避免双重求值
这个优化策略很好理解,我们可能都已经不知不觉地运用在了实际的编程中:
// not use this
setTimeout('alert("hello world")', 1000);
// use this
setTimeout(function() {
alert('hello world');
}, 1000);
上面的两段代码都能执行,但是我们一般用第二种,同样的对于setInterval来说也是如此,类似的还有eval()以及Function()构造函数。JavaScript像其他很多脚本语言一样,允许你在程序中提取一个包含代码的字符串,然后动态执行它。当你在JavaScript代码中执行另一段JavaScript代码时,都会导致双重求值的性能消耗,此代码首先会以正常的方式求值,然后在执行过程中对包含于字符串中的代码发起另一个求值运算。所以,大多数时候没必要使用eval()和Function(),就避免使用它们;至于setTimeout和setInterval,建议传入函数而不是字符串来作为第一个参数。
由此联想到了php中的''和“”,我们尽量用''去引用字符串,也是同样的原因。
使用Object/Array直接量
// use this
var obj = {
name: 'hanzichi',
age: 10
};
var arr = [0, 1, 2, 3];
// not use this
var obj = new Object();
obj.name = 'hanzichi';
obj.age = 10;
var arr = new Array();
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3;
不要重复工作
也许最常见的重复工作就是浏览器探测(当然“不要重复工作”只是一种思想,并不一定针对浏览器探测)。
考虑一个添加事件处理器的例子,典型的跨浏览器代码写法如下:
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');
});
方法在第一次被调用时,会先检查并决定使用哪种方法去绑定事件处理器,然后原始函数被包含正确操作的新函数覆盖。最后一步调用新的函数(也可以直接return 新的函数),并传入原始参数。之后的每次调用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');
});
addHandler(window, 'keydown', function() {
console.log('key down');
});
条件预加载确保所有函数消耗的时间相同,其代价是需要在脚本加载时就检测,而不是加载后。预加载适用于一个函数马上就要被用到,并且在整个页面的生命周期中频繁出现的场合。
常见的“不要重复工作”还有做ajax时候的浏览器探测,自己可以思考下写写代码。
使用速度快的部分
- 位运算
用位运算加速大家都应该熟悉并且熟练掌握:
// use &1 instead of %2
var a = 10;
if (a & 1) { // use this
// ...
}
if (a % 2) { // not use this
// ...
}
// use << 1 instead of *2
var a = 10;
var b = a << 1; // use this
var b = a * 2; // not use this
var a = 10;
var b = a >> 1; // use this
var b = a / 2; // not use this
位运算除了能加速快,还能用在各种算法和数据结构中,比如状态压缩dp,按位dp等等。
- 原生方法
无论你的JavaScript代码如何优化,都永远不会比JavaScript引擎提供的原生方法更快,因为JavaScript原生部分在你写代码前就已经存在在浏览器中了,并且都是用底层语言写的,诸如C++。这意味着这些方法会被编译成机器码,成为浏览器的一部分。
所以尽量用一些内置的函数或者常量,比如Math对象提供的:
Math.E Math.LN10 Math.LN2 Math.PI Math.SQRT1_2 Math.abs() Math.sin() Math.sqrt() . . .
另外一个例子是选择器API,它允许使用CSS选择器来查找DOM节点。CSS查询被JavaScript原生支持并被JQuery发扬光大。JQuery引擎被广泛认为是最快的CSS查询引擎,但是它仍然比原生方法慢。原生的querySelector()和querySelectorAll()方法完成任务平均所需时间是基于JavaScript的CSS查询的10%。所以当原生方法可用时,尽量使用它们。特别是数学运算和DOM操作,用编译后的代码做更多的事情,你的代码就会越快。
高性能JavaScript 编程实践的更多相关文章
- 读高性能JavaScript编程 第二章 让我知道了代码为什么要这样写
代码为什么要这样写? function initUI(){ var doc = document, bd = doc.body, links = doc.getElementsByTagName_r( ...
- 读高性能JavaScript编程学英语 第一章第三页第一段话
When the browser encounters a <script> tag, as in this HTML page, there is no way of knowing w ...
- 读高性能JavaScript编程 第四章 Conditionals
if else 和 switch && 递归 if else 和 switch 一般来说,if-else 适用于判断两个离散的值或者判断几个不同的值域.如果判断多于两个离散 ...
- 读高性能JavaScript编程 第三章
第三章 DOM Scripting 最小化 DOM 访问,在 JavaScript 端做尽可能多的事情. 在反复访问的地方使用局部变量存放 DOM 引用. 小心地处理 HTML 集合,因为他们表现 ...
- 读高性能JavaScript编程 第一章
草草的看完第一章,虽然看的是译文也是感觉涨姿势了, 我来总结一下: 由于 大多数浏览器都是 single process 处理 ui updatas and js execute 于是产生问题: js ...
- 读高性能JavaScript编程 第四章 Duff's Device
又要开始罗里吧嗦的 第四章 Summary 了. 这一次我尽量精简语言. 如果你认为 重复调用一个方法数次有点辣眼睛的话 比如: function test(i){ process(i++); pr ...
- 高性能javascript学习总结(2)--DOM编程
我们知道,对DOM的操作都是非常的耗性能的,那么为什么会耗性能呢? 文档对象模型(DOM)是一个独立于语言的,使用 XML和 HTML 文档操作的应用程序接口(API).在浏览器中,主要与 ...
- 高性能javascript学习总结(3)--数据访问
在 JavaScript 中,数据存储位置可以对代码整体性能产生重要影响.有四种数据访问类型:直接量,变量,数组项,对象成员. 直接量仅仅代表自己,而不存储于特定位置. JavaScr ...
- 高性能javascript学习笔记系列(5) -快速响应的用户界面和编程实践
参考高性能javascript 理解浏览器UI线程 用于执行javascript和更新用户界面的进程通常被称为浏览器UI线程 UI线程的工作机制可以理解为一个简单的队列系统,队列中的任务按顺序执行 ...
随机推荐
- 仿腾讯QQ竖直滑动导航菜单
菜单就像qq软件的分组,鼠标经过自动显示相应组的内容. 效果体验网址:http://keleyi.com/a/bjad/nf86w2dv.htm 以下是源代码: <html> <he ...
- javascript 函数初探 (一)--- 神马是函数
神马是函数? 所谓函数,本质上是一种代码的分组形式.我们可以通过这种形式赋予某组代码一个名字,以便与之后的调用.下面,我们来示范以下函数的声明: function sum(a, b){ var c = ...
- SharePoint 快捷获取列表栏内部名称
在列表设置页面点击浏览器书签获取列表字段内部名称,使用效果如下图: 如何使用: 修改浏览器上任意书签的url地址为以下代码,注意:代码中不能有换行符 javascript:(function(){va ...
- Atitit.报名模块的管理
Atitit.报名模块的管理 1.1. 统计报名数据1 1.2. 存储1 1.3. 报名1 1.4. 查看报名数据3 1.1. 统计报名数据 select count(*) as nums from ...
- Clion 跨平台的C++ IDE
CLion 是 JetBrains 推出的全新的 C/C++ 跨平台集成开发环境. 正式版本已经发出,目前是1.0.1 http://www.jetbrains.com/clion/ http://b ...
- jQuery修改class属性和CSS样式
jQuery修改class属性和CSS样式 class属性修改 类属性即class属性,规定类名. 用类选择器规定样式的时候,需要为元素指定类名,即class属性的值. 注意每个HTML元素只有一个c ...
- C++实现Ping
这是一个老话题了,但是我刚学会... 我们的目的是实现这么个东西: 之所以用红框框一下是因为,从baidu.com到123.125.114.144的过程是DNS解析,我们暂时先实现ping的部分. 基 ...
- iOS开发工程师面试题(一)
SDWEBImge原理 一,先上标答 1)UIImageView+WebCache: setImageWithURL:placeholderImage:options: 先显示 placeholde ...
- iOS检查App新版本并更新新版本
检查新版本 更新 第一种方法 //检查新版本 更新 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ...
- 敏捷开发与jira之阶段工作项概述
每次迭代都分这5个阶段,但每个阶段的时间根据版本情况定,最终目标是:第一个阶段拿到交付范围,在第五个阶段都完成,并拿到本次版本团队所消耗的工时. Jira是项目过程管理的一种手段,跟多体现在工时跟踪, ...