JavaScript质量陷阱小结_javascript本事_脚本之家

1.避免使用eval或者Function构造函数 2.避免使用with
3.不要在性能要求关键的函数中使用try-catch-finally 4.避免使用全局变量
5.避免在性能要求关键的函数中使用for-in 6.使用字符串累加计算风格
7.原操作会比函数调用快 8.设置setTimeout 时传递函数名而不是字符串
9.避免在对象中使用不需要的DOM引用 10.最清晰的目标速度,最小化作用域链
11.试着在脚本中少使用注释,避免使用长变量名
12.在当前作用域存储应用的外部变量 13.使用变量缓存值
1.避免使用eval或者Function构造函数
使用eval或者Function构造函数的代价是非常昂贵的,每次都需要脚本引擎转换源代码到可执行代码。
此外,使用eval处理字符串必须在运行时解释。 运行缓慢的代码: 复制代码 代码如下: function addMethod(object,
property, code) { object[property] = new Function; } addMethod(myObj,
‘methodName’, ‘this.localVar=foo’); 运行更快的代码: 复制代码 代码如下: function addMethod(object,
property, func) { object[property] = func; } addMethod(myObj,
‘methodName’, function () { ‘this.localVar=foo’; }); 2.避免使用with
尽管很方便,with需要附加的查找引用时间,因为它在编译的时候并不知道作用域的上下没。
运行缓慢的代码: 复制代码 代码如下: with
{ foo = ‘Value of foo property of object’; bar = ‘Value of bar property
of object’; } 运行更快的代码: 复制代码
代码如下: var myObj = test.object; myObj.foo = ‘Value of foo property of
object’; myObj.bar = ‘Value of bar property of object’;
3.不要在性能要求关键的函数中使用try-catch-finally
try-catch-finally在运行时每次都会在当前作用域创建一个新的变量,用于分配语句执行的异常。
异常处理应该在脚本的高层完成,在异常不是很频繁发生的地方,比如一个循环体的外面。
如果可能,尽量完全避免使用try-catch-finally。 运行缓慢的代码: 复制代码 代码如下: var object = [‘foo’,
‘bar’], i; for (i = 0; i < object.length; i++) { try { // do
something that throws an exception } catch { // handle exception } }
运行更快的代码: 复制代码 代码如下: var
object = [‘foo’, ‘bar’], i; try { for (i = 0; i < object.length;
i++) { // do something } } catch { // handle exception }
4.避免使用全局变量
如果你在一个函数或者其它作用域中使用全局变量,脚本引擎需要遍历整个作用域去查找他们。
全局作用域中的变量在脚本的生命周期里都存在,然后局部范围的会在局部范围失去的时候被销毁。
运行缓慢的代码: 复制代码 代码如下: var
i, str = ”; function globalScope() { for { str += i; // here we
reference i and str in global scope which is slow } } globalScope();
运行更快的代码: 复制代码 代码如下:
function localScope() { var i, str = ”; for { str += i; // i and str in
local scope which is faster } } localScope();
5.避免在性能要求关键的函数中使用for-in
for-in循环需要脚本引擎建立一张所有可枚举属性的列表,并检查是否与先前的重复。
如果你的for循环作用域中的代码没有修改数组,可以预先计算好数组的长度用于在for循环中迭代数组。
运行缓慢的代码: 复制代码 代码如下: var
sum = 0; for { sum += arr[i]; } 运行更快的代码: 复制代码 代码如下: var sum = 0; for (var i = 0,
len = arr.length; i < len; i++) { sum += arr[i]; }
6.使用字符串累加计算风格
使用+运算会在内存中创建一个新的字符串并把连接的值赋给它。仅仅是将这个结果赋值给一个变量。
为了避免连接结果的中间变量,可以使用+=来直接赋值结果。 运行缓慢的代码:
复制代码 代码如下: a += ‘x’ + ‘y’;
运行更快的代码: 复制代码 代码如下: a +=
‘x’; a += ‘y’; 7.原操作会比函数调用快
可以考虑在性能要求关键的循环和函数中使用可以替代的原操作。
运行缓慢的代码: 复制代码 代码如下: var
min = Math.min; 运行更快的代码: 复制代码
代码如下: var min = a < b ? a : b; arr[arr.length] = val;
8.设置setTimeout 时传递函数名而不是字符串
如果你传递一个字符串到setTimeout中,字符串将会被eval计算而导致缓慢。
使用一个匿名函数包装来代替,这样在编译的时候就可以被解释和优化。
运行缓慢的代码: setInterval(‘doSomethingPeriodically;
setTimeOut(‘doSomethingAfterFiveSeconds; 运行更快的代码: 复制代码 代码如下:
setInterval(doSomethingPeriodically, 1000);
setTimeOut(doSomethingAfterFiveSeconds, 5000);
9.避免在对象中使用不需要的DOM引用 不要这么做: 复制代码 代码如下: var car = new Object();
car.color = “red”; car.type = “sedan” 更好的一种形式: 复制代码 代码如下: var car = { color : “red”;
type : “sedan” } 10.最清晰的目标速度,最小化作用域链 低效率方法:
复制代码 代码如下: var url =
location.href; 一种高效形式: 复制代码
代码如下: var url = window.location.href;
11.试着在脚本中少使用注释,避免使用长变量名
尽可能的保证注释少或者避免使用注释,特别是在函数,循环以及数组中。
注释不必要的减缓脚本执行并且增加了文件大小。比如: 不建议的形式: 复制代码 代码如下: function someFunction() {
var person_full_name=”somename”; /* stores the full name*/ }
更好的写法: 复制代码 代码如下: function
someFunction() { var name=”somename”; }
12.在当前作用域存储应用的外部变量
当一个函数被执行的运行上下问被穿件,一个活动的对象会包含所有局部变量会被推到上下文链的前面。
在作用域链中,最慢的是清楚的识别标识符,意味着局部变量是最快的。存储频繁使用的外部变量读和写都会明显的加快。这对于全局变量和其他深层次的标识符查找特别明显。
同样,在当前作用域中的变量比对象像属性的访问速度快。 运行缓慢的代码:
复制代码 代码如下: function doSomething {
var divs = document.getElementsByTagName, text = [‘foo’, /* … n …
*/, ‘bar’]; for (var i = 0, l = divs.length; i < l; i++) {
divs[i].innerHTML = text[i]; } } 运行更快的代码: 复制代码 代码如下: function doSomethingFaster {
var doc = document, divs = doc.getElementsByTagName, text = [‘foo’, /*
… n … */, ‘bar’]; for (var i = 0, l = divs.length; i < l; i++)
{ divs[i].innerHTML = text[i]; } }
如果你需要访问一个元素在一个大的循环中,使用一个本地的DOM访问会更快。
运行更快的代码: 复制代码 代码如下:
function doSomethingElseFaster() { var get =
document.getElementsByTagName; for (var i = 0, i < 100000; i++) {
get; } } 13.使用变量缓存值 在做重复工作的地方使用局部变量缓存值。
下面的一组例子表明了存储值到局部变量的广泛意义。
例子1.计算执行前在循环体内使用变量存储数学函数 错误的方法: 复制代码 代码如下: var d=35; for { y +=
Math.sin*10; } 更好的处理: 复制代码
代码如下: var d = 55; var math_sind = Math.sin*10; for { y +=
math_sind; } 例子2.保存数组的长度在循环中使用 糟糕的处理:
数组的长度每次都会被重复计算 复制代码
代码如下: for (var i = 0; i < arr.length; i++) { // do something }
更好的改进: 更好的方法是保存数组的长度 复制代码 代码如下: for (var i = 0, len =
arr.length; i < len; i++) { // do something }
总的来说,如果已经做了一次,我们就不需要重复的做不必要的工作。例如,作用域或者函数中多次使用到计算的一个表达式的值,保存到变量可以使它多次被使用,否则我们会过头的声明一个变量并赋值然后只适用一次。所以请记住这些。
补充说明:第2点 顺便说一下JS主要不是编译的是解释的.
虽说不影响表达,但学术还是严谨点好. 第6点这是不是格式搞乱了? a += ‘x’ +
‘y’; 运行更快的代码: a += ‘x’; a += ‘y’;
9.避免在对象中使用不需要的DOM引用 new Object也是DOM引用?
这个应该是说不要轻意使用new Object, 以及new Function() 一般情况使用
{…}, […], f = function{…} 即: 这和上面那一点应该说的是一回事.
最后补充一个少用 位运算,
因为js的所有数值运算最后都是全部转得浮点来算的..所以位运算可能反而更慢.

JavaScript 是一个比较完善的前端开发语言,在现今的 web
开发中应用非常广泛,尤其是对 Web 2.0 的应用。随着 Web 2.0
越来越流行的今天,我们会发现:在我们的 web 应用项目中,会有大量的
JavaScript 代码,并且以后会越来越多。JavaScript
作为一个解释执行的语言,以及它的单线程机制,决定了性能问题是 JavaScript
的软肋,也是 web 软件工程师们在写 JavaScript
需要高度重视的一个问题,尤其是针对 Web 2.0 的应用。绝大多数 web
软件工程师都或多或少的遇到过所开发的 Web 2.0
应用的性能欠佳的问题,其主要原因就是 JavaScript
性能不足,浏览器负荷过重。但是,解决这种解释执行并且单线程运作语言的性能问题也并非易事。这篇文章会着重介绍一些关于开发中
JavaScript 性能调优的技巧和最佳实践,同样也会涉及到关于 JavaScript 操作
DOM 节点的性能调优的一些方法 .

简介
Web 开发中经常会遇到性能的问题,尤其是针对当今的 Web2.0 应用。JavaScript
是当今使用最为广泛的 Web 开发语言,Web
应用的性能问题很大一部分都是由程序员写的 JavaScript
脚本性能不佳所造成的,里面包括了 JavaScript 语言本身的性能问题,以及其与
DOM
交互时的性能问题。本文主要来探讨一下如何尽可能多的避免这类问题,从而最大限度的提高
Web 应用的性能。

JavaScript 性能调优
JavaScript
语言由于它的单线程和解释执行的两个特点,决定了它本身有很多地方有性能问题,所以可改进的地方有不少。

eval 的问题
比较下述代码:
清单 1. eval 的问题

复制代码 代码如下:

var reference = {}, props = “p1”;
eval(“reference.” + props + “=5”)
var reference = {}, props = “p1”;
reference[props] = 5

有“eval”的代码比没有“eval”的代码要慢上 100 倍以上。
主要原因是:JavaScript
代码在执行前会进行类似“预编译”的操作:首先会创建一个当前执行环境下的活动对象,并将那些用
var 申明的变量设置为活动对象的属性,但是此时这些变量的赋值都是
undefined,并将那些以 function
定义的函数也添加为活动对象的属性,而且它们的值正是函数的定义。但是,如果你使用了“eval”,则“eval”中的代码(实际上为字符串)无法预先识别其上下文,无法被提前解析和优化,即无法进行预编译的操作。所以,其性能也会大幅度降低。

Function 的用法
比较下述代码:
清单 2. function 的用法

复制代码 代码如下:

var func1 = new Function(“return arguments[0] + arguments[1]”);
func1(10, 20);
var func2 = function(){ return arguments[0] + arguments[1] };
func2(10, 20);

这里类似之前提到的“eval”方法,这里“func1”的效率会比“func2”的效率差很多,所以推荐使用第二种方式。

函数的作用域链(scope chain):
JavaScript
代码解释执行,在进入函数内部时,它会预先分析当前的变量,并将这些变量归入不同的层级(level),一般情况下:
局部变量放入层级 1(浅),全局变量放入层级 2(深)。如果进入“with”或“try

catch”代码块,则会增加新的层级,即将“with”或“catch”里的变量放入最浅层(层
1),并将之前的层级依次加深。
参考如下代码:
清单 3. 函数作用域链

复制代码 代码如下:

var myObj = … ..
… ..
function process(){
var images = document.getElementsByTagName(“img”),
widget = document.getElementsByTagName(“input”),
combination = [];
for(var i = 0; i < images.length; i++){
combination.push(combine(images[i], widget[2*i]));
}
myObj.container.property1 = combination[0];
myObj.container.property2 = combination[combination.length-1];
}

这里我们可以看到,“images”,“widget”,“combination”属于局部变量,在层
1。“document”,“myObj”属于全局变量,在层 2。
变量所在的层越浅,访问(读取或修改)速度越快,层越深,访问速度越慢。所以这里对“images”,“widget”,“combination”的访问速度比“document”,“myObj”要快一些。所以推荐尽量使用局部变量,可见如下代码:
清单 4. 使用局部变量

复制代码 代码如下:

var myObj = … ..
… ..
function process(){
var doc = document;
var images = doc.getElementsByTagName(“img”),
widget = doc.getElementsByTagName(“input”),
combination = [];
for(var i = 0; i < images.length; i++){
combination.push(combine(images[i], widget[2*i]));
}
myObj.container.property1 = combination[0];
myObj.container.property2 = combination[combination.length-1];
}

我们用局部变量“doc”取代全局变量“document”,这样可以改进性能,尤其是对于大量使用全局变量的函数里面。
再看如下代码:
清单 5. 慎用 with

复制代码 代码如下:

var myObj = … ..
… ..
function process(){
var doc = document;
var images = doc.getElementsByTagName(“img”),
widget = doc.getElementsByTagName(“input”),
combination = [];
for(var i = 0; i < images.length; i++){
combination.push(combine(images[i], widget[2*i]));
}
with (myObj.container) {
property1 = combination[0];
property2 = combination[combination.length-1];
}
}

加上“with”关键字,我们让代码更加简洁清晰了,但是这样做性能会受影响。正如之前说的,当我们进入“with”代码块时,“combination”便从原来的层
1 变到了层 2,这样,效率会大打折扣。所以比较一下,还是使用原来的代码:
清单 6. 改进 with

复制代码 代码如下:

var myObj = … ..
… ..
function process(){
var doc = document;
var images = doc.getElementsByTagName(“img”),
widget = doc.getElementsByTagName(“input”),
combination = [];
for(var i = 0; i < images.length; i++){
combination.push(combine(images[i], widget[2*i]));
}
myObj.container.property1 = combination[0];
myObj.container.property2 = combination[combination.length-1];
}

但是这样并不是最好的方式,JavaScript 有个特点,对于 object
对象来说,其属性访问层级越深,效率越低,比如这里的“myObj”已经访问到了第
3 层,我们可以这样改进一下:
清单 7. 缩小对象访问层级

复制代码 代码如下:

var myObj = … ..
… ..
function process(){
var doc = document;
var images = doc.getElementsByTagName(“img”),
widget = doc.getElementsByTagName(“input”),
combination = [];
for(var i = 0; i < images.length; i++){
combination.push(combine(images[i], widget[2*i]));
}
var ctn = myObj.container;
ctn.property1 = combination[0];
ctn.property2 = combination[combination.length-1];
}

我们用局部变量来代替“myObj”的第 2
层的“container”对象。如果有大量的这种对对象深层属性的访问,可以参照以上方式提高性能。

字符串(String)相关
字符串拼接
经常看到这样的代码:
清单 8. 字符串简单拼接