首先,了解几个概念:

字面量:它只代表自身,不存储在特定的位置。JavaScript中的字面量有:字符串、数字、布尔值、对象、数组、函数、正则,以及特殊的null和undefined值

本地变量:使用var 定义的数据单元

数组元素:存储在JavaScript数组对象内部,以数字作为索引

对象成员:存储在JavaScript对象内部,以字符串作为索引。

每一种数据存储的位置都有不同的读写消耗。大多数情况下,从一个字面量和局部变量中存取数据的性能差异是微不足道的。访问数组元素和对象成员的代价则要高一些,高多少主要取决于浏览器。

字面量和局部变量的访问速度快于数组项和对象成员的访问速度。

通常的建议是尽量使用字面量和局部变量,减少数字向和对象成员的使用。为此有下边几种模式可优化代码:

管理作用域

每一个JavaScript函数都表示为一个对象,确切的说是Function对象的一个实例。Function对象拥有可编程访问的属性,和一系列不能通过代码访问仅供JavaScript引擎存取的内部属性。其中一个就是[[scope]]即作用域链。它决定了哪些属性能被函数访问。函数作用域中的每个对象都被称为一个可变对象,每个可变对象都以键值对的形式存在。每一个变量都会经历一次标识符解析过程,该过程搜索执行期的作用域链,这个搜索过程会影响性能。

标识符的解析是有代价的,在执行环境的作用域链中,一个标识符所在的位置越深,它的读写速度也就越慢。

因此函数中读写局部变量总是最快的。读写全局变量是最慢的。全局变量总是存在于执行环境作用域链的做末端,是最远的。

建议尽可能的使用局部变量。一个经验法则:如果某个跨作用域的值在函数中被引用了一次以上,那么就把它存储到局部变量里。

function initUI(){
var bd = document.body,
links = document.getElementsByTagName("a"),
i = 0,
len = links.length;
... ...
} //在方法里多次用到document这个全局对象,可以把它的引用存放在一个局部变量中,让局部变量代替全局变量。访问全局变量的次数由2次变成了1次
function initUI(){
var doc = document,
bd = doc.body,
links = doc.getElementsByTagName("a"),
i = 0,
len = links.length;
... ...
}

改变作用域链

有两个语句在执行时会改变作用域链。

一个是with语句:with语句避免了多次书写同一个全局变量,但是会产生性能问题。

function initUI(){
  with(document){
    var bd = body,
  links = getElementsByTagName("a"),
  i = 0,
  len = links.length;
  ... ...
  }
}

当程序执行到with语句时,执行环境的作用域链临时被改变了。一个新的变量对象被创建,它包含了参数指定的所有属性。这个对象被推入作用域链的首位,这意味着函数的所有局部变量现在都位于第二个作用域链对象中,因此访问的代价更高了:访问document对象的属性非常快,而访问局部变量则变慢了,比如bd

另一个是try-catch语句中的catch子句:也具有with同样的效果

try{
  methodThatMightCauseAnError();
}catch(ex){
  alert(ex.message);//作用域链在此处改变
}

try代码块中发生错误,执行过程会自动跳转到catch子句,然后把异常对象推入一个变量对象并置于作用域的首位。在catch代码块内部,函数所有局部变量将会放在第二个作用域链对象中。一旦catch子句执行完毕,作用域链就会返回之前的状态。

try-catch语句是一个非常有用的语句 不建议完全弃用。可以尽量简化代码使catch对子句的影响最小化,可以将错误处理委托给一个函数来处理

try{
  methodThatMightCauseAnError();
}catch(ex){
  handleError(ex);
}

由于只执行一条语句,没有局部变量的访问,作用域链的临时改变就不会影响代码性能。

动态作用域

无论是with语句还是try-catch还是包含eval()的函数,都被认为是动态作用域。动态作用域只存在于代码执行过程中,因此无法通过静态分析(查看代码结构)检测出来。

经过优化的JavaScript引擎。比如safari的Nitro引擎,尝试通过分析代码来确定哪些变量可以在特定的时候被访问。这些引擎试图避开传统作用域链的查找,用标识符索引的方式进行快速查找来代替。但是当遇到动态作用域时就失效了,脚本引擎必须切换回较慢的基于哈希表的标识符识别方式,这更像是传统的作用域链查找。

推荐:只有在确实有必要时才使用动态作用域。

闭包、作用域和内存

由于闭包的[[scope]]属性包含了与执行环境作用域链相同的对象的引用,因此函数的激活对象不会随着执行环境一同销毁。这意味着脚本中的闭包和非闭包函数相比,需要更多的内存开销。在大型WEB应用中,这可能是个问题。尤其是IE浏览器中需要关注,由于IE使用非原生JavaScript对象来实现DOM对象,因此闭包会导致内存泄漏。

脚本编程中,最好小心的使用闭包,它同时关系到内存和执行速度。

对象成员

对象在原型链中存在的位置越深,找到它也就越慢。每深入一层原型链都会增加性能损失,搜索实例成员比从字面量或局部变量中读取数据代价更高,并且还有遍历原型链带来的开销,这些让性能问题更为严重。

嵌套成员,即点操作符:window.location.href。每次遇到点操作符,嵌套成员会导致JavaScript引擎搜索所有对象成员。

对象成员嵌套的越深,读取速度就会越慢。执行location.href总是比window.location.href要快,后者也比window.location.href.toString()要快。如果这些属性不是对象的实例属性,那么成员解析还需要搜索原型链,这会花更多的时间。

大部分浏览器中,通过点表示法(object.name)操作和通过括号表示法(object["name"])操作并没有明显的区别。只有在Safari中,点符号始终会更快,但这并不意味着不要用括号符号。

缓存对象成员值

所有类似的性能问题都与对象成员有关,因此应该尽可能避免使用他们,或者应该说,只在必要时使用对象成员。(在同一个函数中没有必要多次读取同一个对象成员)

通常来说,如果函数中要多次读取同一个对象的属性,最佳做法是讲=将属性值保存到局部变量中。局部变量能用来替代属性以避免多次查找带来的性能开销。特别是在处理嵌套对象成员时,这样做会明显提升执行速度。

不要再同一个函数中多次查找同一个对象成员,除非它的值改变了。

总结:在JavaScript中,数据存储的位置会对代码整体性能产生重大的影响。数据存储共有4种方式:字面量、变量、数组项、对象成员。

1、访问字面量和局部变量的速度最快,相反访问数组元素和对象成员相对较慢。

2、由于局部变量存在于作用链的起始位置,因此访问局部变量比访问跨作用域变量更快。变量在作用域链中的位置越深,访问所需要时间就越长。由于全局变量总处在作用域链的最末端,因此访问速度也是最慢的

3、避免使用with语句,因为它会改变执行环境作用域链。同样的try-catch语句中的catch子句也有同样的影响,因此要小心使用。

4、嵌套的对象成员会明显影响性能,尽量少用。

5、属性或方法在原型链中的位置越深,访问它的速度也越慢。

6、通常来说,你可以把常用的对象成员、数组元素、跨域变量保存在局部变量中来改善JavaScript性能,因为局部变量访问速度更快。

JS性能优化——数据存取的更多相关文章

  1. js性能优化-事件委托

    js性能优化-事件委托 考虑一个列表,在li的数量非常少的时候,为每一个li添加事件侦听当然不会存在太多性能方面的问题,但是当列表非常的长,长到上百上千甚至上万的时候(当然只是一个解释,实际工作中很少 ...

  2. js 性能优化利器:prepack

    1. js 性能优化 js 本身是没有像 python 一样的预编译功能,更没有像 java 一样的编译功能,所以,这里所说的 js 代码预编译 只是通过工具实现的类似功能而已. 这就要提到 prep ...

  3. js 性能优化 篇一

    JS性能优化 摘自:http://www.china125.com/design/js/3631.htm  首先,由于JS是一种解释型语言,执行速度要比编译型语言慢得多.(注:,Chrome是第一款内 ...

  4. js性能优化文章集锦

    总结的js性能优化方面的小知识http://www.it165.net/pro/html/201503/35336.html 如何优化你的JS代码http://www.php100.com/html/ ...

  5. JS性能优化笔记搜索整理

    通过网上查找资料了解关于性能优化方面的内容,现简单整理,仅供大家在优化的过程中参考使用,如有什么问题请及时提出,再做出相应的补充修改. 一. 让代码简洁:一些简略的表达方式也会产生很好的优化 eg:x ...

  6. js性能优化--学习笔记

    <高性能网站建设进阶指南>: 1.使用局部变量,避免深入作用域查找,局部变量是读写速度最快的:把函数中使用次数超过一次的对象属性和数组存储为局部变量是一个好方法:比如for循环中的.len ...

  7. 你不知道的Node.js性能优化,读了之后水平直线上升

    本文由云+社区发表 "当我第一次知道要这篇文章的时候,其实我是拒绝的,因为我觉得,你不能叫我写马上就写,我要有干货才行,写一些老生常谈的然后加上好多特技,那个 Node.js 性能啊好像 D ...

  8. Web篇之JS性能优化

    首先,性能优化分好几个方面,本章我们从js方面来优化. 1:垃圾收集 日常中的某些情况下垃圾收集器无法回收无用变量,导致的一个结果就是——内存使用率不断增高,以下为对应的情况以及处理方法. ①对象相互 ...

  9. Ext.js性能优化漫谈

    Ext.js是一个用于建立企业级应用的纯JS框架.毫无疑问,它为我们提供了大量的组件,比如container,panel,field,grid,这些组件使用起来很方便,不需要去写js和html,但是e ...

随机推荐

  1. 聊聊、Zookeeper Windows启动

    Apache ZooKeeper is an effort to develop and maintain an open-source server which enables highly rel ...

  2. iOS UI Element Usage

    Bars The Status Bar

  3. 【spring boot logback】日志logback格式解析

    日志logback格式解析 logback官网 格式解析 https://logback.qos.ch/manual/layouts.html#ClassicPatternLayout 官网格式解析有 ...

  4. Android性能优化第(三)篇---MAT比Menmery Monitor更强大

    作者 LooperJing 2016.11.17 16:42* 字数 1687 阅读 1603评论 3喜欢 21 在Android性能优化第(一)篇---基本概念中讲了JAVA的四大引用,讲了一下GC ...

  5. Zookeeper协调分布式节点demo

    多台服务器和客户端通过第三方组件Zookeeper管理 public class DistributedServer { private static final String connectStri ...

  6. 关于阿里 weex 的使用与案例

    1. 阿里宣布开源Weex http://mp.weixin.qq.com/s?__biz=MzA4MjA0MTc4NQ==&mid=504089541&idx=1&sn=3a ...

  7. 线程间操作无效: 从不是创建控件“XXX”的线程访问它

    方法1 Invoke((MethodInvoker)(()=>{XXX.Text = message;})); 方法2 取消跨线程检查 Control.CheckForIllegalCrossT ...

  8. 伪静态对struts action的重写

    参见 http://ocaicai.iteye.com/blog/1312189 最重要的而是在web.xml中配置 <filter-mapping> <filter-name> ...

  9. C++一元多项式相加

    实验名称:一元多项式相加 // multiply.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream& ...

  10. 文件I/O操作为什么叫输入/出流

    参考以下文档: http://blog.csdn.net/hguisu/article/details/7418161 我们关注的焦点是错误的,重点不在文件,我们关注的核心是数据流. 这种流可以是文本 ...