Web前端性能优化——编写高效的JavaScript

前言

随着计算机的发展,Web富应用时代的到来,Web 2.0早已不再是用div+css高质量还原设计的时代。自Gmail网页版邮件服务的问世开始,Web前端开发也开启了新的纪元。用户需求不断提高,各种新的技术层出不穷,前端工程师的地位也越来越重要。然而任何事物都是有两面性的,随着前端技术的发展,前端业务越来越繁重,这大大增加了JS代码量。因此,要提高Web的性能,我们不仅需要关注页面加载的时间,还要注重在页面上操作的响应速度。那么,接下来我们讨论几种能够提高JavaScript效率的方法。

一、从JavaScript的作用域谈起

当JavaScript代码执行时,JavaScript引擎会创建一个执行环境,又叫执行上下文。执行环境定义了变量或函数有权访问的其他数据,决定了它们的行为,每个执行环境都有一个与它关联的变量对象,环境中定义的所有函数、变量都保存在这个对象中。在页面加载的时候,JavaScript引擎会创建一个全局的执行环境,所有全局变量和函数都是作为window对象(浏览器中)的属性和方法创建的。在此之后,每执行一个函数,JavaScript引擎都会创建一个对应的执行环境,并将该环境放入环境栈中,所以当前正在执行的函数的执行环境是在环境栈的最顶部的,当函数执行完毕之后,其执行环境会弹出栈,并被销毁,保存在其中的变量和函数定义也会被销毁。

当代码在一个执行环境中执行时,JavaScript引擎会创建变量对象的一个作用域链,它可以保证对执行环境有权访问的变量和函数的有序访问。作用域链的前端始终是当前执行的代码所在的环境的变量对象。全局环境的作用域链中只有一个变量对象,它定义了所有可用的全局变量和函数。当函数被创建时,JavaScript引擎会把创建时执行环境的作用域链赋给函数的内部属性[[scope]];当函数被执行时,JavaScript引擎会创建一个活动对象,最开始时这个活动对象只有一个变量,即arguments对象。该活动对象会出现在执行环境作用域链的顶端,接下来是函数[[scope]]属性中的对象。

当需要查找某个变量或函数时,JavaScript引擎会通过执行环境的作用域链来查找变量和函数,从作用域链的顶端开始,如果没找到,则向下寻找直至找到为止。若一直到全局作用域都没有找到,则该变量或函数为undefined。

举个栗子:

function add(a,b) {
    return a + b;
}

var result = add(2,3);

代码执行时,add函数有一个仅包含全局变量对象的[[scope]]属性,add函数执行时,JavaScript引擎创建新的执行环境以及一个包含this、arguments、a、b的活动对象,并将其添加到作用域链中。如下图所示:

二、使用局部变量

了解了作用域链的概念,我们应该知道在查找变量会从作用域链的顶端开始一层一层的向下找。显然,查找的层数越多,花费的时间越多。所以为了提高查找的速度,我们应该尽量使用 局部变量(到目前为止,局部变量是JavaScript中读写最快的标识符)。

例如:

function createEle() {
    document.createElement("div");
}
function createEle() {
    var doc = document;
    doc.createElement("div");
}

当document使用次数比较少时,可能无所谓,可是如果在一个函数的循环中大量使用document,我们可以提前将document变成局部变量。

来看看jquery怎么写的:

(function(window, undefined) {
     var jQuery = function() {}
     // ...
     window.jQuery = window.$ = jQuery;
})(window);

这样写的优势:

1、window和undefined都是为了减少变量查找所经过的scope作用域。当window通过传递给闭包内部之后,在闭包内部使用它的时候,可以把它当成一个局部变量,显然比原先在window scope下查找的时候要快一些。(原来的window处于作用域链的最顶端,查找速度慢)

2、在jquery压缩版本jquery.min.js中可以将局部变量window替换成单个字母,减小文件大小,提高加载速度

3、undefined也是JavaScript中的全局属性。将undefined作为参数传递给闭包,因为没给它传递值,它的值就是undefined,这样闭包内部在使用它的时候就可以把它当做局部变量使用,从而提高查找速度。undefined并不是JavaScript的保留字或者关键字。

4、undefined在某些低版本的浏览器(例如IE8、IE7)中值是可以被修改的(在ECMAScript3中,undefined是可读/写的变量,可以给它赋任意值,这个错误在ECMAScript5中做了修正),将undefined作为参数并且不给它传值可以防止因undefined的值被修改而产生的错误。

三、避免增长作用域链

在JavaScript中,有两种语句可以临时增加作用域链:with、try-catch

with可以使对象的属性可以像全局变量来使用,它实际上是将一个新的变量对象添加到执行环境作用域的顶部,这个变量对象包含了指定对象的所有属性,因此可以直接访问。

这样看似很方便,但是增长了作用域链,原来函数中的局部变量不在处于作用域链的顶端,因此在访问这些变量的时候要查找到第二层才能找到它。当with语句块之行结束后,作用域链将回到原来的状态。鉴于with的这个缺点,所以不推荐使用。

try-catch中的catch从句和with类似,也是在作用域链的顶端增加了一个对象,该对象包含了由catch指定命名的异常对象。但是因为catch语句只有在放生错误的时候才执行,因此影响比较少。

四、字符串链接优化

由于字符串是不可变的,所以在进行字符串连接时,需要创建临时字符串。频繁创建、销毁临时字符串会导致性能低下。

当然,这个问题在新版本浏览器包括IE8+中都得到了优化,所以不需要担心

在低版本浏览器(IE6、IE7)中,我们可以种数组的join方法来代替。

var temp = [];
var i = 0;
temp[i++] = "Hello";
temp[i++] = " ";
temp[i++] ="everyone";

var outcome = temp.join("");

五、条件判断

当出现条件判断时,我们采用什么样的结构才能使性能最优?

if(val == 0) {
    return v0;
}else if(val == 1) {
    return v1;
}else if(val == 2) {
    return v2;
}else if(val == 3) {
    return v3;
}else if(val == 4) {
    return v4;
}

当条件分支比较多时,我们可以斟酌哪种条件出现的概率比较大,并将对应的语句放在最上面,这样可以减少判断次数。

使用switch语句,新版的浏览器基本上都对swi做了优化,这样层数比较深时,性能比if会更好

使用数组:

var v = [v0,v1,v2,v3,v4];
return v[valeue];

要求:对应的结果是单一值,而不是一系列操作

另外,其他方面的优化,譬如

if(condition1) {
    return v1;
}
else {
    return v2
}
// 改成
if(condition1) {
    return v1;
}
return v2;

六、快速循环

1、循环总次数使用局部变量

for( var i = 0;i < arr.length;i++) {

}
// 改成
var len = arr.length;
for( var i = 0;i < len;i++) {

}

这样就避免了每次循环的属性查找。这点尤其重要,因为在进行dom操作时,很多人会这样写:

var divList = document.getElementsByTagName("div");
for( var i = 0;i < divList.length;i++) {

}

查找DOM元素的属性是相对耗时的,所以应该避免这种写法。

2、如果可以,递减代替递增

for(var i = 0;i < arr.length;i++) {

}
// 改成
for(var i = arr.length - 1;i--;) {

}

var i = 0;
while(i < arr.length) {
    i++;
}
// 改成
var i = arr.length - 1;
while(i--) {

}

i=0的时候会直接跳出,循环次数比较多时还是很有用的。

七、展开循环

var i = arr.length - 1;
while(i--) {
    dosomething(arr[i]);
}

遇到这样的情况时,执行一次循环的时候我们可以选择不止执行一次函数。

var interations = Math.floor(arr.length / 8);
var left = arr.length % 8;
var  i = 0;

if(left) {
    do {
        dosomething(arr[i++]);
    } while(--left);
}
do {
    dosomething(arr[i++]);
    dosomething(arr[i++]);
    dosomething(arr[i++]);
    dosomething(arr[i++]);
    dosomething(arr[i++]);
    dosomething(arr[i++]);
    dosomething(arr[i++]);
    dosomething(arr[i++]);
} while(--interations);

当遇到大数组,减少循环的开销,性能不就提上去了嘛。(至于为什么是每次循环,调8次函数,大牛测出来的,这样达到最佳)

八、高效存取数据

JavaScript中4种地方可以存取数据:

字面量值;变量;数组元素;对象属性

字面量值和变量中存取数据是最快的,从数组元素和对象属性中存取数据相对较慢,并且随着深度增加,存取速度会越来越慢,譬如obj.item.value就比obj.item慢。

某些情况下我们可以将对象、数组属性存成局部变量来提高速度,譬如:

for( var i = 0;i < arr.length;i++) {

}
// 改成
var len = arr.length;
for( var i = 0;i < len;i++) {

}
var divList = document.getElementsByTagName("div");
for( var i = 0;i < divList.length;i++) {

}
// 改成
//
var divList = document.getElementsByTagName("div");
for( var i = 0,len = divList.length;i < len;i++) {

}

 

九、事件委托

事件委托就是利用冒泡的原理,将原本应该添加在某些元素身上的监听事件,添加到其父元素身上,来达到提高性能的效果。

举个栗子:

<div>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
        <li>10</li>
    </ul>
</div>
<script>
window.onload = function() {
    var ul = document.getElementsByTagName(‘ul‘)[0];
    var liList = document.getElementsByTagName(‘li‘);

    for(var i = 0,len = liList.length;i < len;i++) {
        liList[i].onclick = function() {
            alert(this.innerHTML);
        }
    }
}
</script> 

这样我们就为每个li添加了监听事件了。

显然,我们通过循环为每个li添加监听事件是不优化的。这样不仅浪费了内存,在新的li加入的时候我们还要重新为它添加监听事件。

我们可以这样写:

<div>
    <ul>
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
        <li>9</li>
        <li>10</li>
    </ul>
</div>
<script>
window.onload = function() {
    var ul = document.getElementsByTagName(‘ul‘)[0];
    var liList = document.getElementsByTagName(‘li‘);

    ul.onclick = function(e) {
        var e = e || window.event;
        var target = e.target || e.srcElement;

        if(target.nodeName.toLowerCase() == "li") {
            alert(target.innerHTML);
        }
    }
}
</script>

这样写的好处:

只添加一个监听事件,节省了内存;新加入li的时候我们也不用为它单独添加监听事件;在页面中添加事件处理程序所需的时候更少,因为我们只需要为一个DOM元素添加事件处理程序。

如果是原创文章,转载请注明出处:http://www.cnblogs.com/MarcoHan/

最后,提一点不会提高性能的建议

if( 2 == value) {

}

类似这种判断的时候推荐常量放在左边,这样就可以预防类似 if( value = 2){} 的错误了,因为如果少写了一个等号, if( value = 2) {} 是合法的语句,而且代码量变大的时候不容易检查出来。if( 2 = value) {} 这样少写了等号JavaScript引擎会直接报错,我们就可以愉快地改过来了。(只是建议)

时间: 2024-10-07 08:13:05

Web前端性能优化——编写高效的JavaScript的相关文章

Web前端性能优化的9大问题

1.请减少HTTP请求基本原理: 在浏览器(客户端)和服务器发生通信时,就已经消耗了大量的时间,尤其是在网络情况比较糟糕的时候,这个问题尤其的突出. 一个正常HTTP请求的流程简述:如在浏览器中输入"www.xxxxxx.com"并按下回车,浏览器再与这个URL指向的服务器建立连接,然后浏览器才能向服务器发送请求信息,服务器在接受到请求的信息后再返回相应的信息,浏览器接收到来自服务器的应答信息后,对这些数据解释执行. 而当我们请求的网页文件中有很多图片.CSS.JS甚至音乐等信息时,将

WEB前端性能优化小结

1. 请减少HTTP请求 基本原理: 在浏览器(客户端)和服务器发生通信时,就已经消耗了大量的时间,尤其是在网络情况比较糟糕的时候,这个问题尤其的突出. 一个正常HTTP请求的流程简述:如在浏览器中输入"www.xxxxxx.com"并按下回车,浏览器再与这个URL指向的服务器建立连接,然后浏览器才能向服务器发送请求信息,服务器在接受到请求的信息后再返回相应的信息,浏览器接收到来自服务器的应答信息后,对这些数据解释执行. 而当我们请求的网页文件中有很多图片.CSS.JS甚至音乐等信息时

WEB前端性能优化常见方法

web前端是应用服务器处理之前的部分,前端主要包括:HTML,CSS,javascript,image等各种资源,针对不同的资源有不同的优化方式. 1. 内容优化 (1)减少HTTP请求数:这条策略是最重要最有效的,因为一个完整的请求要经过DNS寻址,与服务器建立连接,发送数据,等待服务器响应,接收数据这样一个消耗时间成本和资源成本的复杂的过程. 常见方法:合并多个CSS文件和js文件,利用CSS Sprites整合图像,Inline Images(使用 data:URL scheme在实际的页

Web 前端性能优化相关内容解析

Web 前端性能优化相关内容,来源于<Google官方网页载入速度检测工具PageSpeed Insights 使用教程>一文中PageSpeed Insights 的相关说明.大家可以对照着去优化自己的网站或者相关项目.本文由Jeff 整理. 0.提高服务器的响应速度 砸钱的东西,但却最根本:搞好这一项,甚比下面任何一项. 1.优化样式表和脚本的排列顺序 正确地排列外部样式表与外部和内嵌脚本的顺序,可增加下载时同时加载的数据量,并提高浏览器显示网页的速度. 将样式表放在顶部,将脚本放在底部

Web 前端性能优化【转】

Web 前端性能优化是个大话题,是个值得运维人员持续跟踪的话题,是被很多网站无情忽视的技术. Web 前端优化最佳实践之 内容篇Web 前端优化最佳实践之 Server 篇Web 前端优化最佳实践之 Cookie 篇Web 前端优化最佳实践之 CSS 篇Web 前端优化最佳实践之 JavaScript 篇Web 前端优化最佳实践之 图象篇Web 前端优化最佳实践之 Mobile(iPhone) 篇 Yahoo! 的 Exceptional Performance team 在 Web 前端方面作

Web前端性能优化全攻略[转载]

1. 尽量减少  ">HTTP 请求 (Make Fewer  ">HTTP Requests) 作为第一条,可能也是最重要的一条.根据 Yahoo! 研究团队的数据分析,有很大一部分用户访问会因为这一条而取得最大受益.有几种常见的方法能切实减少  ">HTTP 请求: 1) 合并文件,比如把多个  ">CSS 文件合成一个: 2)  ">CSS Sprites 利用  ">CSS background 相关元

Web前端性能优化全攻略

网页制作poluoluo文章简介:Web 前端性能优化是个大话题,是个值得运维人员持续跟踪的话题,是被很多网站无情忽视的技术. Web 前端性能优化是个大话题,是个值得运维人员持续跟踪的话题,是被很多网站无情忽视的技术. Yahoo! 的 Exceptional Performance team 在 Web 前端方面作出了卓越的贡献.广为人知的优化规则也由 13 条到 14 条,再到 20 条,乃至现在的 34 条--真是与时俱进啊.最新的 34 条也针对不同的角度做了分类. 面向内容的优化规则

Web 前端性能优化相关内容解析[转]

Web 前端性能优化相关内容,来源于<Google官方网页载入速度检测工具PageSpeed Insights 使用教程>一文中PageSpeed Insights 的相关说明.大家可以对照着去优化自己的网站或者相关项目.本文由Jeff 整理. 0.提高服务器的响应速度 砸钱的东西,但却最根本:搞好这一项,甚比下面任何一项. 1.优化样式表和脚本的排列顺序 正确地排列外部样式表与外部和内嵌脚本的顺序,可增加下载时同时加载的数据量,并提高浏览器显示网页的速度. 将样式表放在顶部,将脚本放在底部

性能优化——Web前端性能优化

核心知识点: 1.排查网站性能瓶颈的手法:分析各个环节的日志,找出异常部分 2.Web前端:网站业务逻辑之前的部分(浏览器.图片服务.CDN) 3.优化手段 1)浏览器优化 (1)减少http请求 a.http请求的开销:建立通信链路.进行数据传输.,同时要启动独立的线程去处理. b.减少http的手段:合并CSS.合并JavaScript,合并图片,这样浏览器就只有一次请求. (2)使用浏览器缓存 a.将静态资源缓存到客户浏览器上, b.更新时是生成新的JavaScript文件,并更改html