资讯专栏INFORMATION COLUMN

译文:Efficient JavaScript

YPHP / 2319人阅读

摘要:所以会导致一些性能上的问题当异常被捕获的时候。注意对象的转换字面量,比如字符串,数字或者布尔值,在中有两种表现,它门可能被当作单纯的值或者一个对象。使用字符串的累加形式。

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

高效的JavaScript

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

ECMAScript 避免使用evalFunction构造函数

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

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

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

错误的使用eval:

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

正确的姿势:

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

错误的使用Function constructor

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"
);

正确的姿势:

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,所以会导致性能上的问题。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85631.html

相关文章

  • 2017-09-21 前端日报

    摘要:前端日报精选的作用鸟瞰前端再论性能优化翻译给创始人和们的许可协议解惑如何工作引擎深入探究优化代码的个技巧译文第期还是,让我来解决你的困惑中文基础为什么比快二分查找法你真的写对了吗个人文章推荐机不可失直播技术盛宴,深圳腾讯开发者大 2017-09-21 前端日报 精选 setTimeout(fn, 0) 的作用鸟瞰前端 , 再论性能优化翻译:给创始人和 CTO 们的 React 许可协议...

    kidsamong 评论0 收藏0
  • 不再彷徨:完全弄懂JavaScript中的this(译文总结)

    摘要:至于,其只是以数组的方传入参数,其它部分是一样的,如下它们也可用于在中的类继承中,调用父级构造器。间接调用,调用了父级构造器对比方法和,它俩都立即执行了函数,而函数返回了一个新方法,绑定了预先指定好的,并可以延后调用。 其实this是一个老生常谈的问题了。关于this的文章非常多,其实我本以为自己早弄明白了它,不过昨天在做项目的过程中,还是出现了一丝疑惑,想到大概之前在JavaScri...

    YanceyOfficial 评论0 收藏0
  • 译文JavaScript中类数组对象

    摘要:所以我说的这些类数组对象是什么它们有一些,其中包括是一个很特殊的变量,你再所有函数体内都可以访问到。让类数组对象成为一个数组当然这个标题是不太准确的,假如我们需要将这些类数组对象变成数组一样,我们需要建立一个新的数组。 它看起来像是一个数组,而且它有一个length属性,然而它并不是一个数组。JavaScript有时候是一门很怪异的语言,因为你很难定义一个数组的概念而没有什么例外的。所...

    lbool 评论0 收藏0
  • JavaScript 事件循环(译文JavaScript Event Loop)

    摘要:事件循环了解了在引擎中是如何工作了之后,来看下如何使用异步回调函数来避免代码。从回调函数被放入后秒钟,把移到中。由于事件循环持续地监测调用栈是否已空,此时它一注意到调用栈空了,就调用并创建一个新的调用栈。 听多了JavaScript单线程,异步,V8,便会很想去知道JavaScript是如何利用单线程来实现所谓的异步的。我参考了一些文章,了解到一个很重要的词汇:事件循环(Event L...

    K_B_Z 评论0 收藏0
  • Javascript闭包入门(译文)

    摘要:也许最好的理解是闭包总是在进入某个函数的时候被创建,而局部变量是被加入到这个闭包中。在函数内部的函数的内部声明函数是可以的可以获得不止一个层级的闭包。 前言 总括 :这篇文章使用有效的javascript代码向程序员们解释了闭包,大牛和功能型程序员请自行忽略。 译者 :文章写在2006年,可直到翻译的21小时之前作者还在完善这篇文章,在Stackoverflow的How do Java...

    Fourierr 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<