高性能的JavaScript--数据访问(1)
数据存储在哪里,关系到代码运行期间数据被检索到的速度。在JavaScript中,此问题相对简单,因为数据存储只有少量方式可供选择。正如其他语言那样,数据存储位置关系到访问速度。在JavaScript中有四种基本的数据访问位置:
1.Literal values 直接量
直接量仅仅代表自己,而不存储于特定位置。 JavaScript的直接量包括:字符串,数字,布尔值,对象,数组,函数,正则表达式,具有特殊意义的空值,以及未定义。
2.Variables 变量
开发人员使用var关键字创建用于存储数据值。
3.Array items 数组项
具有数字索引,存储一个JavaScript数组对象。
4.Object members 对象成员
具有字符串索引,存储一个JavaScript对象。
每一种数据存储位置都具有特定的读写操作负担。大多数情况下,对一个直接量和一个局部变量数据访问的性能差异是微不足道的。访问数组项和对象成员的代价要高一些,具体高多少,很大程度上依赖于浏览器。总的来说,直接量和局部变量的访问速度要快于数组项和对象成员的访问速度。,如果关心运行速度,那么尽量使用直接量和局部变量,限制数组项和对象成员的使用。
1.作用域链和标识符解析
每一个JavaScript函数都被表示为对象。进一步说,它是一个函数实例。函数对象正如其他对象那样,拥有你可以编程访问的属性,和一系列不能被程序访问,仅供JavaScript引擎使用的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义。内部[[Scope]]属性包含一个函数被创建的作用域中对象的集合。此集合被称为函数的作用域链,它决定哪些数据可由函数访问。此函数作用域链中的每个对象被称为一个可变对象,每个可变对象都以“键值对”的形式存在。当一个函数创建后,它的作用域链被填充以对象,这些对象代表创建此函数的环境中可访问的数据。
当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain,不简称sc)来保证对执行环境有权访问的变量和函数的有序访问。作用域第一个对象始终是当前执行代码所在环境的变量对象(VO)
例如:
function add(x,y){
var b=x+y;
return b;
}
当add()函数创建后,它的作用域链中填入一个单独的可变对象,此全局对象代表了所有全局范围定义的变量。此全局对象包含诸如窗口、浏览器和文档之类的访问接口。
如图:
上图就是函数Add()的作用域链。
Add函数的作用域链将在运行时用到。如果运行下面的代码
var total = add(5, 10);
运行此add函数时建立一个内部对象,称作“运行期上下文”。一个运行期上下文定义了一个函数运行时的环境。对函数的每次运行而言,每个运行期上下文都是独一的。所以每次调用同一个函数就会导致多处调用上下文。当函数执行完毕,运行期上下文就被销毁。
一个运行期上下文有它自己的作用域链,用于标示符解析。当运行期上下文被创建时,它的作用域被初始化,连同运行函数的[[Scope]]属性中所包含的对象。这些值按照它们出现在函数中的顺序,被复制到运行期上下文的作用域链中。这项工作一旦完成,一个被称作“激活对象”的新对象就为运行期上下文创建好了。此激活对象作为函数执行期的一个可变对象,包含访问所有局部变量,命名参数,参数集合,和this的接口,然后,这个对象被推入作用域的前端。当作用域链被销毁时,激活对象也一同销毁。
上图是运行时Add()函数的作用域链。
在函数运行过程中,没遇到一个变量,标识符识别过程要决定从哪里获得或者存储数据,此过程收索运行期上下文的作用域链,查找同名的标识符。搜索工作从运行函数的激活目标之作用域链的前端开始。如果找到了,那么就使用这个具有指定标识符的变量,如果没有找到,搜索工作将进入作用域链的下一个对象。此过程持续进行,直到找到标示符。如果整个过程都没有找到那么被认为是undefined。正是这种搜索过程影响了性能。
2.标识符解析的性能
标示符识别不是免费的,事实上没有哪种电脑操作可以不产生性能开销。在运行期山下文的作用域链中,一个标示符所处的位置越深,它的读写速度就越慢。所以,函数中局部变量的访问速度总是最快的,而全局变量通常是最慢的(优化的JavaScript引擎在某些情况下可以改变这种情况,如谷歌浏览器)。全局变量总是处于运行前上下文作用域链的最后一个位置。所以总是最远才能触及。
用局部变量存储本地范围之外的变量值,如果它们在函数中的使用多于一次。考虑下面的例子:
function initUI(){
var
bd = document.body,
links = document.getElementsByTagName_r("a"),
i = 0,
len = links.length;
while(i < len){ update(links[i++]); } document.getElementById("go-btn").onclick = function(){ start(); }; bd.className = "active";
}
此函数包括三个对document的引用,document是一个全局对象。搜索此变量,必须遍历整个作用域链,指导最后在全局变量对象中找到它。你可以通过这种方法减轻重复的全局变量访问对性能的影响;首先将全局变量的引用放在一个局部变量中,然后使用整个局部变量代替全局变量。 代码重写如下:
function initUI(){ var doc = document,
bd = doc.body,
links = doc.getElementsByTagName_r("a"),
i = 0,
len = links.length;
while(i < len){ update(links[i++]);
} doc.getElementById("go-btn").onclick = function(){
start();
}; bd.className = "active";
}
initUI()的新版本首先将document的引用存入局部变量doc中,现在访问全局变量的次数是1次,而不是3次。用doc替代document更快,因为它是一个局部变量。当然,这个简单的函数不回显示出巨大的性能改进,因为数量的原因。不过如果几十个全局变量被反复访问,那么性能改进将明显的多么出色。
3.改变作用域链
一般来说,一个运行期上下文的作用域链不会突然被改变。但是,有两种表达式可以在运行时临时改变运行期上下文作用域链。第一个是with表达式。
with表达式为所有的对象属性创建了一个默认操作变量。在其他语言中,类似的功能通常用来避免书写一些重复的代码。initUI()函数可以重写成如下样式:
function initUI(){
with (document){ //avoid!
var bd = body,
links = getElementsByTagName_r("a"),
i = 0,
len = links.length;
while(i < len){
update(links[i++]);
}
getElementById("go-btn").onclick = function(){
start();
};
bd.className = "active";
}
}
此重写的initUI()版本使用了一个with表达式,避免多次书写document,这看起来似乎更有效率,而实际上却产生了一个性能问题。
当代码流执行到一个with表达式时,运行期上下文的作用域链被临时改变了。一个新的可变对象将被创建,她包含指定对象的所有属性。此对象被插入到作用域链的前端,意味着现在函数的所有局部变量都被推入第二个作用域链对象中,所以访问代价更高了。
通过将document对象传递给with表达式,一个新的可变对象容纳了document对象的所有属性,被插入到作用域链的前端。这使得访问document的属性非常快,但是访问局部变量的速度却变慢了,例如bd变量。正因为这个原因,最好不要使用with表达式。正如前面提到的,只要简单地将document存储在一个局部变量中,就可以获得性能上的提升。
在JavaScript中不只是with表达式人为地改变运行期上下文的作用域链,try-catch表达式的catch子句具有相同效果。当try块发生错误时,程序流程自动转入catch块,并将异常对象推入作用域链前端的一个可变对象中。在catch块中,函数的所有局部变量现在被放在第二个作用域链对象中。例如:
try {
methodThatMightCauseAnError();
} catch (ex){
alert(ex.message); //scope chain is augmented here
}
但是,只要catch子句执行完毕,作用域链就会返回到原来的状态。
如果使用得当,try-catch表达式是非常有用的语句,所以不建议完全避免。如果你计划使用一个try-catch语句,请确保你了解可能发生的错误。一个try-catch语句不应该作为JavaScript错误的解决办法。如果你知道一个错误会经常发生,那说明应当修正代码本身的问题。
你可以通过精缩代码的办法最小化catch子句对性能的影响。一个很好的模式是将错误交给一个专用函数来处理。如:
try {
methodThatMightCauseAnError();
} catch (ex){
handleError(ex); //delegate to handler method
}
handleError()函数是catch子句中运行的唯一代码。此函数以适当方法自由地处理错误,并接收由错误产生的异常对象。由于只有一条语句,没有局部变量访问,作用域链临时改变就不会影响代码的性能。
高性能的JavaScript--数据访问(1)的更多相关文章
- JavaScript 数据访问(通译自High Performance Javascript 第二章) [转]
JavaScript 数据访问(通译自High Performance Javascript 第二章) JavaScript 数据访问(翻译自High Performance Javascript ...
- Javascript高性能编程-提高数据访问速度
hasOwnProperty()仅检索实例不检索原型,in即检索实例,又检索原型 成员嵌套越深,访问速度越慢,只在必要的情况下使用对象成员. 如果在同一个函数中你要多次读 ...
- 高性能js之数据访问性能
js中si中基本数据访问: 直接量, 变量, 数组项, 对象成员 性能问题: 首先要理解作用域链的基本概念,例如,当一个函数被创建时,就会产生一个激活对象(AO对象),AO对象中存储了该函数中所有的属 ...
- 高性能JavaScript笔记一(加载和执行、数据访问、DOM编程)
写在前面 好的书,可能你第一遍并不能领会里面的精魂,当再次细细品评的时候,发现领悟的又是一层新的含义 (这段时间,工作上也不会像从前一样做起来毫不费力,开始有了新的挑战,现在的老大让我既佩服又嫉妒,但 ...
- 高性能Javascript--高效的数据访问
接上一篇,希望能写一个高性能Javascript专题. 第一篇:高性能Javascript--脚本的无阻塞加载策略. 参考摘录<高性能Javascript>. 经典计算机科学的一个问题是, ...
- 分享自己的超轻量级高性能ORM数据访问框架Deft
Deft 简介 Deft是一个超轻量级高性能O/R mapping数据访问框架,简单易用,几分钟即可上手. Deft包含如下但不限于此的特点: 1.按照Transact-SQL的语法语义风格来设计,只 ...
- 超轻量级高性能ORM数据访问组件Deft,比dapper快20%以上
超轻量级高性能ORM数据访问组件Deft,比dapper快20%以上 阅读目录 Deft简介 Deft 核心类介绍 Deft 3分钟即可上手使用 其他可选的配置参数 性能测试 Demo代码下载 回到顶 ...
- 高性能的JavaScript -- 读书笔记
高性能的JavaScript 一. 加载和运行 将脚本放在底部 脚本下载解析执行时,页面已经加载完成并显示在用户面前 成组脚本 减少外部脚本文件数量,整合成一个文件 延迟脚本 动态脚本元素 ...
- ClownFish:比手写代码还快的通用数据访问层
http://www.cnblogs.com/fish-li/archive/2012/07/17/ClownFish.html 阅读目录 开始 ClownFish是什么? 比手写代码还快的执行速度 ...
- Dojo Data Store——统一数据访问接口
原文地址:http://www.infoq.com/cn/articles/wq-dojo-data-store 无论在传统的桌面应用还是在主流的互联网应用中,数据始终占据着软件应用中的核心地位.当下 ...
随机推荐
- 在Unity中使用UGUI修改Mesh绘制几何图形
在商店看到这样一个例子,表示很有兴趣,他们说是用UGUI做的.我想,像这种可以随便变形的图形,我第一个就想到了网格变形. 做法1: 细心的朋友应该会发现,每个UGUI可见元素,都有一个‘Canvas ...
- OpenFOAM&Gmsh&CFD圆柱绕流(两个圆柱)
问题: 圆柱绕流问题,模拟仿真有两个圆柱.一个源的流体变化情况. 解决步骤: 1.使用Gmsh画出网格,并保存cylindertwo.msh 2.以Cavity为基础创建新的Case:Cylinder ...
- java文件上传
jsp界面代码: <body> <form action="servlet/UploadServlet" enctype="multipart/for ...
- spark 官方文档(1)——提交应用程序
Spark版本:1.6.2 spark-submit提供了在所有集群平台提交应用的统一接口,你不需要因为平台的迁移改变配置.Spark支持三种集群:Standalone.Apache Mesos和Ha ...
- gitlab使用个人版v16.11
title: gitlab使用个人版v16.11 date: 2016-11-13 20:53:00 tags: [gitlab] --- 1.安装gitbash 附上地址链接:git 2.配置git ...
- 转载请注明出处: https://github.com/qiu-deqing/FE-interview
转载请注明出处: https://github.com/qiu-deqing/FE-interview Table of Contents generated with DocToc FE-inter ...
- Ruby中实现module继承
module FooModule def self.included base base.extend ClassMethods end module ClassMethods def ...
- 【河北省队互测】 gcd BZOJ 2818
Description 给定整数N,求1<=x,y<=N且Gcd(x,y)为素数的 数对(x,y)有多少对. Input 一个整数N Output 如题 Sample Input 4 Sa ...
- matlab绘图基础
matlab绘制条形图并分组显示: a =[1 2 3] b =[4 5 6] >> d=[a;b] d = 1 2 3 4 5 6 >> bar(d,'group') 修改横 ...
- 高性能MySQL(二):创建高性能索引
) not null); insert into city_demo(city) select city from city insert into city_demo(city) select ci ...