专栏名称: 程序员大咖
为程序员提供最优质的博文、最精彩的讨论、最实用的开发资源;提供最新最全的编程学习资料:PHP、Objective-C、Java、Swift、C/C++函数库、.NET Framework类库、J2SE API等等。并不定期奉送各种福利。
目录
相关文章推荐
51CTO技术栈  ·  突发!刚被OpenAI收购就惨遭Claude ... ·  昨天  
稀土掘金技术社区  ·  掘金 AI 编程社区- 人人都是 AI 编程家竞赛 ·  5 天前  
稀土掘金技术社区  ·  URL地址末尾加不加”/“有什么区别 ·  4 天前  
稀土掘金技术社区  ·  为了让 iframe 支持 ... ·  3 天前  
51好读  ›  专栏  ›  程序员大咖

如何使JavaScript更高效

程序员大咖  · 公众号  · 程序员  · 2017-04-11 19:01

正文

请到「今天看啥」查看全文


(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;
  }
}


隔离 eval 和 with 的使用

由于这些结构会对性能造成显著影响,应该尽可能的少用它们。但有时候你可能仍然需要使用到它们。如果某个函数被反复调用,或者某个循环在重复执行,那最好不要在它们内部使用这些结构。它们只适合在执行一次或很少几次的代码中使用,还要注意这些代码对性能要求不高。


无论什么情况,尽量将它们与其它代码隔离开来,这样就不会影响到其它代码的性能。比如,把它们放在一个顶层函数中,或者只运行一次并把结果保存下来,以便稍后可以使用其结果而不必再运行这些代码。


try-catch-finally 结构可能会在某些浏览器对性能产生影响,包括 Opera,所以你最好以同样的方式对其进行隔离。


尽量不用全局变量

创建临时变量很简单,所以很诱人。然而,因为某些原因,它可能会让脚本运行缓慢。


首先,如果代码在函数或另一个作用域中引用全局变量,脚本引擎会依次通过每个作用域直到全局作用域。局部变量找起来会快得多。


全局作用域中的变量存在于脚本的整个生命周期。而局部变量会在离开局部作用域的时候被销毁,它们占用的内存可以被垃圾收集器回收。


最后,全局作用域由 window 对象共享,也就是说它本质上是两个作用域而不是一个。在全局作用域中,变量总是通过其名称来定位,而不是像局部变量那样经过优化,通过预定义的索引来定位。这最终导致脚本引擎需要花更多时间来找到全局变量。


函数通常也在全局作用域中创建。因此一个函数调另一个函数,另一个函数再接着调其它函数,如此深入下去,脚本引擎就会不断增加往回定位全局变量的时间。


来看个简单的示例,i 和 s 定义在全局作用域中,而函数会使用这些全局变量:

var i, s = '';
function testfunction() {  for(i = 0; i < 20; i++) {
    s += i;
  }
}
testfunction();

下面的替代版本执行得更快。在多数当今的浏览器中,包括 Opera 9、最新的 Internet Explorer、Firefox、Konqueror 和 Safari,它的执行速度会比之前的版本快 30% 左右。

function testfunction() {
  var i, s = '';  for(i = 0; i < 20; i++) {
    s += i;
  }
}
testfunction();

注意对象的隐式转换

字面量,比如字符中、数、和布尔值,在 ECMAScript 中有两种表现形式。它们每种类型都可以作为值创建,也可以作为对象创建。比如,var oString = 'some content'; 创建了一个字符串值,而 var oString = new String('some content'); 创建了等价的字符串对象。


所有属性和方法都是在字符串对象而不是值上定义的。如果你对字符串值调用属性和方法,ECMAScript 引擎必须用相同的字符串值隐式地创建一个新的字符串对象,然后才能调用方法。这个对象仅用于这一个需求,如果下次再对字符串值调用某个方法,会再次类似地创建字符串对象。


下面的示例的让脚本引擎创建 21 个新的字符串对象。每次访问 length 属性和每次调用 charAt 方法的时候都会创建对象:

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

下面的示例与上面那个示例等价,但只创建了一个对象,它的执行结果更好:

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

如果你的代码经常调用字面量值的方法,你就应该考虑把它们转换为对象,就像上面的例子那样。


注意,虽然本文中大部分观点都与所有浏览器相关,但这种优化主要针对 Opera。它也可能影响其它一些浏览器,但在 Internet Explorer 和 Firefox 中可能会慢一些。


在要求性能的函数中避免使用 for-in

for-in 循环经常被误用,有时候普通的 for 循环会更合适。for-in 循环需要脚本引擎为所有可枚举的属性创建一个列表,然后检查其中的重复项,之后才开始遍历。


很多时候脚本本身已经知道需要遍历哪睦属性。多数情况下,简单的 for 循环可以逐个遍历那些属性,特别是它们使用有序的数字作为名称的时候,比如数组,或者伪数组(像由 DOM 创建的 NodeList 就是伪数组)。


下面有一个未正确使用 for-in 循环的示例:

var oSum = 0;for(var i in oArray) {
  oSum += oArray[i];
}

使用 for 循环会更有效率:

var oSum = 0






请到「今天看啥」查看全文