译文:Efficient JavaScript

原文连接:https://dev.opera.com/articles/efficient-javascript/?page=2#primitiveo...javascript

高效的JavaScript

曾经,一个Web页面不会包含太多的脚本,或者至少来讲,它们不会影响页面的性能。然而,如今的Web页面愈来愈像本地运用了,脚本的性能成了一个很大的影响随着愈来愈多的运用转向使用Web技术时,提升页面的性能成为了愈来愈重要的问题。java

ECMAScript

避免使用evalFunction构造函数

每次当evalFunction constructor经过字符串源码形式调用时,脚本引擎必须开启转换机制将字符串源码转换成可执行的代码。一般来讲这是比较耗性能的。浏览器

eval调用特别的很差,当执行的内容字符串传递给eval时不能被提早执行,因为代码执行会被eval中执行的内容影响,那就意味着编译器不能更好的优化执行上下文,而且浏览器在运行时时放弃执行下面的上下文。这样就增长了额外的性能影响。函数

对于Function constructor来讲,它的名声和eval同样也不太好,虽然使用它并不会影响上下文的执行,可是它执行的效率却很低下。示例代码以下:性能

错误的使用eval:优化

function getProperty(oString) {
    var oReference;
    eval('oReference = test.prop.' + oString);
    return oReference;
}

正确的姿式:this

function getProperty(oString) {
    return test.prop[oString];
}

错误的使用Function constructorcode

function addMethod(oObject, oProperty, oFunctionCode) {
    oObject[oProperty] = new Function(oFunctionCode);
}
addMethod(
    myObject,
    'rotateBy90',
    'this.angle = (this.angle + 90) % 360'
);
addMethod(
    myObject,
    'rotateBy60',
    'this.angle = (this.angle + 60) % 360'
);

正确的姿式:orm

function addMethod(oObject, oProperty, oFunction) {
    oObject[oProperty] = oFunction;
}
addMethod(
    myObject,
    'rotateBy90',
    function() {
        this.angle = (this.angle + 90) % 360;
    }
);
addMethod(
    myObject,
    'rotateBy60',
    function() {
        this.angle = (this.angle + 60) % 360;
    }
);

避免使用with

尽管对于开发人员来讲,使用with比较方便,可是对性能来讲,倒是很是消耗的。缘由是对脚本引擎来讲,它会拓展做用域链,而查找变量的时候不会判断是否被当前引用。尽管这种状况带来性能的开销比较少,可是每次编译的时候咱们都不知道内容的做用域,那就意味着编译器不能对其进行优化,因此它就和普通的做用域同样。对象

一种更有效的方法代替方法是使用一个对象变量来代替with的使用。属性的访问能够经过对象的引用来实现。这样工做起来很是有效,若是属性不是基本类型外,好比字符串和布尔值。

考虑下面的代码:

with(test.information.settings.files) {
    primary = 'names';
    secondary = 'roles';
    tertiary = 'references';
}

使用下面的方式效率更高:

var testObject = test.information.settings.files;
testObject.primary = 'names';
testObject.secondary = 'roles';
testObject.tertiary = 'references';

不要在循环的函数里面使用try-catch-finally

try-catch-finally语句相对于其余的语句来讲它的结构很是惟一的,当脚本运行的时候它会在当前的做用域总建立一个变量,这发生在catch语句调用的时候.捕获的异常对象会关联这个变量,这个变量不会存在其余的脚本里,即便是相同的做用域。它在catch语句开始的时候建立,在执行结束的时候销毁它。

由于这个变量会在运行的时候建立和销毁,因此会产生一种特殊的状况,一些浏览器不能及时的在性能比较耗的循环中及时捕获该句柄。因此会致使一些性能上的问题当异常被捕获的时候。

若是可能的话,异常的执行应该在更高的级别执行,这样它就不会频繁的出现,或者经过检查指望的动做最先被容许的话来避免,下面经过示例来讲明:

错误的使用方式:

var oProperties = [
    'first',
    'second',
    'third',
    …
    'nth'
];
for(var i = 0; i < oProperties.length; i++) {
    try {
        test[oProperties[i]].someproperty = somevalue;
    } catch(e) {
        …
    }
}

在许多状况下,try-catch-finally结构能够移动到循环的外围, 这样看起来彷佛语意上有点改变。因为异常抛出时,循环会被中断,可是下面的代码会依然执行。

var oProperties = [
    'first',
    'second',
    'third',
    …
    'nth'
];
try {
    for(var i = 0; i < oProperties.length; i++) {
        test[oProperties[i]].someproperty = somevalue;
    }
} catch(e) {
    …
}

在某些状况下,try-catch-finally结构能够避免使用。好比:

var oProperties = [
    'first',
    'second',
    'third',
    …
    'nth'
];
for(var i = 0; i < oProperties.length; i++) {
    if(test[oProperties[i]]) {
        test[oProperties[i]].someproperty = somevalue;
    }
}

隔离evalwith的使用

因为这些结构影响性能如此的深,因此它们使用的越少越好。可是有时候你可能须要它们,若是一个函数被调用或者一个循环重复的被计算,最好的方式仍是避免使用这些结构,他们最好的解决方案就是被执行一次,或者极少数,以致于基本上对性能没什么影响。

避免使用全局变量

在全局范围内建立一个变量是很诱惑的,只因它建立的方式很简单,可是有一下几个缘由会致使脚本运行变慢。

首先,全局变量须要脚本引擎查找到最外的做用域,查找速度比较慢,第二,全局变量经过window对象被分享,意味着本质上它有两层做用域(??)。

注意对象的转换

字面量,好比字符串,数字或者布尔值,在ECMAScript中有两种表现,它门可能被看成单纯的值或者一个对象。

任何属性或者方法被调用的时候针对的是这个对象,不是这个值,当你引用一个属性或者方法的时候,ECMAScript引擎会暗中的建立一个你值对应的字符串对象。在方法调用以前。这个对象只会被请求一次,当你尝试下一次调用该值的某个方法时它又会被建立一次。来看看下面的例子:

var s = '0123456789';
for(var i = 0; i < s.length; i++) {
    s.charAt(i);
}

上面的例子须要脚本引擎建立21次字符串对象,一次length属性的访问,一次charAt方法的调用。

优化的方案以下所示:

var s = new String('0123456789');
for(var i = 0; i < s.length; i++) {
    s.charAt(i);
}

和上面等效,可是仅仅手动建立了一个对象,性能上要比上面的好不少。

注意:不一样的浏览器,对于装箱和拆箱的优化不同。

避免咋性能堪忧的函数里使用for-in迭代

for-in迭代有它本身的特色,可是它常常被滥用,这种迭代须要脚本引擎建立一个全部可枚举属性的清单,并检出为看成副本,在开始枚举的时候。

使用字符串的累加形式。

字符串的拼接是个昂贵的过程,当使用"+"运算符时,它不会把结果当即添加到变量中,反而它会建立一个新的字符串对象在内存中,并把获得的结果赋值个这个字符串。而后这个新的字符串对象在赋值给变量。可是使用"+="能够避免这样的过程。

原始的操做符可能比函数调用更快

示例:

var min = Math.min(a,b);
A.push(v);

下面的方式和上面的等效,可是效率更高:

var min = a < b ? a : b;
A[A.length] = v;

传递一个回调函数而不是字符串给setTimeout()setInterval()

setTimeout()setInterval()方法传递的是个字符串时,它内部会调用eval,因此会致使性能上的问题。

相关文章
相关标签/搜索