`
yeminping
  • 浏览: 176975 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

编写高性能的JavaScript

    博客分类:
  • web
阅读更多

 

一.执行效率
1. DOM
1.1
使用 DocumentFragment 优化多次 append
说明:添加多个 dom 元素时,先将元素 append DocumentFragment 中,最后统一将 DocumentFragment 添加到页面。
该做法可以减少页面渲染 dom 元素的次数。经 IE Fx 下测试,在 append1000 个元素时,效率能提高 10%-30% Fx 下提升较为明显。

服用前:
for (var i = 0; i < 1000; i++) {
    var el = document.createElement('p');
    el.innerHTML = i;
    document.body.appendChild(el);
}

服用后:
var frag = document.createDocumentFragment();
for (var i = 0; i < 1000; i++) {
    var el = document.createElement('p');
    el.innerHTML = i;
    frag.appendChild(el);
}
document.body.appendChild(frag);

1.2
通过模板元素 clone ,替代 createElement
说明:通过一个模板 dom 对象 cloneNode ,效率比直接创建 element 高。
性能提高不明显,约为 10% 左右。在低于 100 个元素 create append 操作时,没有优势。

服用前:
var frag = document.createDocumentFragment();
for (var i = 0; i < 1000; i++) {
    var el = document.createElement('p');
    el.innerHTML = i;
    frag.appendChild(el);
}
document.body.appendChild(frag);   

服用后:
var frag = document.createDocumentFragment();
var pEl = document.getElementsByTagName('p')[0];
for (var i = 0; i < 1000; i++) {
    var el = pEl.cloneNode(false);
    el.innerHTML = i;
    frag.appendChild(el);
}
document.body.appendChild(frag);

1.3
使用一次 innerHTML 赋值代替构建 dom 元素
说明:根据数据构建列表样式的时候,使用设置列表容器 innerHTML 的方式,比构建 dom 元素并 append 到页面中的方式,效率有数量级上的提高。

服用前:
var frag = document.createDocumentFragment();
for (var i = 0; i < 1000; i++) {
    var el = document.createElement('p');
    el.innerHTML = i;
    frag.appendChild(el);
}
document.body.appendChild(frag);

服用后:
var html = [];
for (var i = 0; i < 1000; i++) {
    html.push('<p>' + i + '</p>');
}
document.body.innerHTML = html.join('');

1.4
使用 firstChild nextSibling 代替 childNodes 遍历 dom 元素
说明:约能获得 30%-50% 的性能提高。逆向遍历时使用 lastChild previousSibling

服用前:
var nodes = element.childNodes;
for (var i = 0, l = nodes.length; i < l; i++) {
var node = nodes[i];
……
}

服用后:
var node = element.firstChild;
while (node) {
……
node = node.nextSibling;
}


2.
字符串

2.1
使用 Array 做为 StringBuffer ,代替字符串拼接的操作
说明: IE 在对字符串拼接的时候,会创建临时的 String 对象;经测试,在 IE 下,当拼接的字符串越来越大时,运行效率会急剧下降。 Fx Opera 对字符串拼接操作进行了优化;经测试,在 Fx 下,使用 Array join 方式执行时间约为直接字符串拼接的 1.4 倍。

服用前:
var now = new Date();
var str = '';
for (var i = 0; i < 10000; i++) {
    str += '123456789123456789';
}
alert(new Date() - now);

服用后:
var now = new Date();
var strBuffer = [];
for (var i = 0; i < 10000; i++) {
    strBuffer.push('123456789123456789');
}
var str = strBuffer.join('');
alert(new Date() - now);


3.
循环语句

3.1
将循环控制量保存到局部变量
说明:对数组和列表对象的遍历时,提前将 length 保存到局部变量中,避免在循环的每一步重复取值。

服用前:
var list = document.getElementsByTagName('p');
for (var i = 0; i < list.length; i++) {
    ……
}

服用后:
var list = document.getElementsByTagName('p');
for (var i = 0, l = list.length; i < l; i++) {
    ……
}


3.2
顺序无关的遍历时,用 while 替代 for
说明:该方法可以减少局部变量的使用。比起效率优化,更能直接看到的是字符数量的优化。该做法有程序员强迫症的嫌疑。

服用前:
var arr = [1,2,3,4,5,6,7];
var sum = 0;
for (var i = 0, l = arr.length; i < l; i++) {
    sum += arr[i];
}   

服用后:
var arr = [1,2,3,4,5,6,7];
var sum = 0, l = arr.length;
while (l--) {
    sum += arr[l];
}

4.
条件分支

4.1
将条件分支,按可能性顺序从高到低排列
说明:可以减少解释器对条件的探测次数。

4.2
在同一条件子的多( >2 )条件分支时,使用 switch 优于 if
说明: switch 分支选择的效率高于 if ,在 IE 下尤为明显。 4 分支的测试, IE switch 的执行时间约为 if 的一半。

4.3
使用三目运算符替代条件分支
服用前:
if (a > b) {
num = a;
} else {
num = b;
}

服用后:
num = a > b ? a : b;


5.
定时器

5.1
需要不断执行的时候,优先考虑使用 setInterval
说明: setTimeout 每一次都会初始化一个定时器。 setInterval 只会在开始的时候初始化一个定时器

服用前:
var timeoutTimes = 0;
function timeout () {
    timeoutTimes++;
    if (timeoutTimes < 10) {
        setTimeout(timeout, 10);
    }
}
timeout();

服用后:
var intervalTimes = 0;
function interval () {
    intervalTimes++;
    if (intervalTimes >= 10) {
        clearInterval(interv);
    }
}
var interv = setInterval(interval, 10);

5.2
使用 function 而不是 string
说明:如果把字符串作为 setTimeout setInterval 的参数,浏览器会先用这个字符串构建一个 function

服用前:
var num = 0;
setTimeout('num++', 10);   

服用后:
var num = 0;
function addNum () {
    num++;
}
setTimeout(addNum, 10);


6.
其他

6.1
尽量不使用动态语法元素
说明: eval Function execScript 等语句会再次使用 javascript 解析引擎进行解析,需要消耗大量的执行时间。

6.2
重复使用的调用结果,事先保存到局部变量
说明:避免多次取值的调用开销。

服用前:
var h1 = element1.clientHeight + num1;
var h2 = element1.clientHeight + num2;

服用后:
var eleHeight = element1.clientHeight;
var h1 = eleHeight + num1;
var h2 = eleHeight + num2;

6.3
使用直接量
说明:
var a = new Array(param,param,...) -> var a = []
var foo = new Object() -> var foo = {}
var reg = new RegExp() -> var reg = /.../

6.4
避免使用 with
说明: with 虽然可以缩短代码量,但是会在运行时构造一个新的 scope
OperaDev
上还有这样的解释,使用 with 语句会使得解释器无法在语法解析阶段对代码进行优化。对此说法,无法验证。

服用前:
with (a.b.c.d) {
property1 = 1;
property2 = 2;
}


服用后:
var obj = a.b.c.d;
obj.property1 = 1;
obj.property2 = 2;


6.5
巧用 || && 布尔运算符
重要程度: ★★★

服用前:
function eventHandler (e) {
if(!e) e = window.event;
}


服用后:
function eventHandler (e) {
e = e || window.event;
}


服用前:
if (myobj) {
doSomething(myobj);
}


服用后:
myobj && doSomething(myobj);


6.6
类型转换
说明:
1).   
数字转换成字符串,应用 "" + 1 ,性能上: ("" +) > String() > .toString() > new String()
2).   
浮点数转换成整型,不使用 parseInt() parseInt() 是用于将字符串转换成数字,而不是浮点数和整型之间的转换,建议使用 Math.floor() 或者 Math.round()
3).   
对于自定义的对象,推荐显式调用 toString() 。内部操作在尝试所有可能性之后,会尝试对象的 toString() 方法尝试能否转化为 String


二.内存管理

2.1
循环引用
说明:如果循环引用中包含 DOM 对象或者 ActiveX 对象,那么就会发生内存泄露。内存泄露的后果是在浏览器关闭前,即使是刷新页面,这部分内存不会被浏览器释放。

简单的循环引用:
var el = document.getElementById('MyElement');
var func = function () {…}
el.func = func;
func.element = el;


但是通常不会出现这种情况。通常循环引用发生在为 dom 元素添加闭包作为 expendo 的时候。

如:
function init() {
    var el = document.getElementById('MyElement');
el.onclick = function () {……}
}
init();


init
在执行的时候,当前上下文我们叫做 context 。这个时候, context 引用了 el el 引用了 function function 引用了 context 。这时候形成了一个循环引用。

下面 2 种方法可以解决循环引用:

1)   
置空 dom 对象

服用前:
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {……}
}
init();


服用后:
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {……}
el = null;
}
init();


el 置空, context 中不包含对 dom 对象的引用,从而打断循环应用。
如果我们需要将 dom 对象返回,可以用如下方法:

服用前:
function init() {
    var el = document.getElementById('MyElement');
    el.onclick = function () {……}
    return el;
}
init();


服用后:
function init() {
var el = document.getElementById('MyElement');
el.onclick = function () {……}
try{
return el;
} finally {
    el = null;
}
}
init();

2)   
构造新的 context
服用前:
function init() {
    var el = document.getElementById('MyElement');
    el.onclick = function () {……}
}
init();


服用后:
function elClickHandler() {……}
function init() {
    var el = document.getElementById('MyElement');
    el.onclick = elClickHandler;
}
init();

function 抽到新的 context 中,这样, function context 就不包含对 el 的引用,从而打断循环引用。

2.2
通过 javascript 创建的 dom 对象,必须 append 到页面中
说明: IE 下,脚本创建的 dom 对象,如果没有 append 到页面中,刷新页面,这部分内存是不会回收的!

示例代码:

    function create () {
        var gc = document.getElementById('GC');
        for (var i = 0; i < 5000 ; i++)
        {
            var el = document.createElement('div');
            el.innerHTML = "test";

            //
下面这句可以注释掉,看看浏览器在任务管理器中,点击按钮然后刷新后的内存变化
            gc.appendChild(el);
        }
    }


2.3
释放 dom 元素占用的内存
说明:
dom 元素的 innerHTML 设置为空字符串,可以释放其子元素占用的内存。
rich 应用中,用户也许会在一个页面上停留很长时间,可以使用该方法释放积累得越来越多的 dom 元素使用的内存。

2.4
释放 javascript 对象
说明:在 rich 应用中,随着实例化对象数量的增加,内存消耗会越来越大。所以应当及时释放对对象的引用,让 GC 能够回收这些内存控件。
对象: obj = null
对象属性: delete obj.myproperty
数组 item :使用数组的 splice 方法释放数组中不用的 item

2.5
避免 string 的隐式装箱
说明:对 string 的方法调用,比如 'xxx'.length ,浏览器会进行一个隐式的装箱操作,将字符串先转换成一个 String 对象。推荐对声明有可能使用 String 实例方法的字符串时,采用如下写法:
var myString = new String('Hello World');

分享到:
评论

相关推荐

    编写高性能JavaScript.doc

    编写高性能JavaScript

    编写高性能JavaScript

    如果你是一个开发者,并且关心内存使用情况与页面性能,你应该了解用户浏览器中的JavaScript引擎是如何运作的。无论是V8,SpiderMonkey的(Firefox)的Carakan(Opera),Chakra(IE)或其他引擎,这样做可以帮助你...

    编写高性能Javascript代码的N条建议

    JavaScript代码在web应用程序中经常用到,但是很多开发者忽视了一些性能方面的知识,如何编写高性能javascript代码呢?接下来,小编跟大家一起学习

    高性能JavaScript 编程pdf电子书(中英文对译)

    《高性能JavaScript》揭示的技术和策略能帮助你在开发过程中消除性能瓶颈。你将会了解如何提升各方面的性能,包括代码的加载、运行、DOM交互、页面生存周期等。雅虎的前端工程师Nicholas C. Zakas和其他五位...

    《高性能JavaScript》

    《高性能JavaScript》揭示的技术和策略能帮助你在开发过程中消除性能瓶颈。你将会了解如何提升各方面的性能,包括代码的加载、运行、DOM交互、页面生存周期等。雅虎的前端工程师Nicholas C. Zakas和其他五位...

    高性能JavaScript 高清完整.pdf版下载

    《高性能JavaScript》揭示的技术和策略能帮助你在开发过程中消除性能瓶颈。你将会了解如何提升各方面的性能,包括代码的加载、运行、DOM交互、页面生存周期等。雅虎的前端工程师Nicholas C. Zakas和其他五位...

    高性能JavaScript

    Zakas和其他五位JavaScript专家介绍了页面代码加载的最佳方法和编程技巧,来帮助你编写更为高效和快速的代码。你还会了解到构建和部署文件到生产环境的最佳实践,以及有助于定位线上问题的工具。

    编写高性能JavaScript(译)

    本文主要关注V8的性能优化,部分内容并不适用于所有JS引擎。最后,转载请注明出处

    高性能JavaScript编程

    《高性能JavaScript》揭示的技术和策略能帮助你在开发过程中消除性能瓶颈。你将会了解如何提升各方面的性能,包括代码的加载、运行、DOM交互、页面生存周期等。雅虎的前端工程师Nicholas C. Zakas和其他五位...

    高性能JavaScript(High Performance JavaScript )

    《高性能JavaScript》揭示的技术和策略能帮助你在开发过程中消除性能瓶颈。你将会了解如何提升各方面的性能,包括代码的加载、运行、DOM 交互、页面生存周期等。雅虎的前端工程师 Nicholas C. Zakas 和其他五位 ...

    高性能JavaScript编程(中英文)

    《高性能JavaScript》揭示的技术和策略能帮助你在开发过程中消除性能瓶颈。你将会了解如何提升各方面的性能,包括代码的加载、运行、DOM交互、页面生存周期等。雅虎的前端工程师Nicholas C. Zakas和其他五位...

    JavaScript高性能编程

    JavaScript高性能编程,非常稀缺的资源,我把背景都去掉了,因为有隐私

    编写可维护的JavaScript中文完整版

    1.作者是大名鼎鼎的NicholasZakas,他是《JavaScript高级程序设计》、《Ajax高级程序设计》和《高性能JavaScript》的作者。在业界很有知名度和影响力,畅销书作者。 2.本书是第一本集中关注编程风格和最佳实践的...

    高性能JavaScript_中英双语版.rar

    《高性能JavaScript》揭示的技术和策略能帮助你在开发过程中消除性能瓶颈。你将会了解如何提升各方面的性能,包括代码的加载、运行、DOM交互、页面生存周期等。雅虎的前端工程师Nicholas C. Zakas和其他五位...

    高性能Javascript编程--英文版

    《高性能JavaScript》揭示的技术和策略能帮助你在开发过程中消除性能瓶颈。你将会了解如何提升各方面的性能,包括代码的加载、运行、DOM交互、页面生存周期等。雅虎的前端工程师Nicholas C. Zakas和其他五位...

Global site tag (gtag.js) - Google Analytics