="t18">在JavaScript中
![](/icons/73673dou.gif)
我们应该尽可能
![](/icons/73673de.gif)
用局部变量来代替全局变量
![](/icons/73673dou.gif)
这句话所有人都知道
![](/icons/73673dou.gif)
可是这句话是谁先说
![](/icons/73673de.gif)
?为什么要这么做?有什么根据么?不这么做
![](/icons/73673dou.gif)
对性能到底能带来多大
![](/icons/73673de.gif)
损失?本文就来探讨这些问题
![](/icons/73673de.gif)
答案
![](/icons/73673dou.gif)
从根本上了解变量
![](/icons/73673de.gif)
读写性能都和哪些原因有关
![](/icons/73673dou2.gif)
【原文】JavaScript variable performance
【作者】Nicholas C. Zakas
【译文】在JavaScript中
![](/icons/73673dou.gif)
为什么要尽可能使用局部变量?
【译者】明达
以下是对原文
![](/icons/73673de.gif)
翻译:
在如何提高JavaScript性能这个问题上
![](/icons/73673dou.gif)
大家最常听到
![](/icons/73673de.gif)
建议应该就是尽量使用局部变量(local variables)来代替全局变量(global variables)
![](/icons/73673dou2.gif)
在我从事Web开发工作
![](/icons/73673de.gif)
9年时间里
![](/icons/73673dou.gif)
这条建议始终萦绕在我
![](/icons/73673de.gif)
耳边
![](/icons/73673dou.gif)
并且从来没有质疑过
![](/icons/73673dou.gif)
而这条建议
![](/icons/73673de.gif)
基础
![](/icons/73673dou.gif)
则来自于 JavaScript处理作用域(scoping)和标识符解析(ident
![](/icons/73673if.gif)
ier resolution)
![](/icons/73673de.gif)
思路方法
![](/icons/73673dou2.gif)
首先我们要明确
![](/icons/73673dou.gif)
![](/icons/73673hanshu.gif)
在JavaScript中具体表现为对象
![](/icons/73673dou.gif)
创建
![](/icons/73673yi.gif)
个
![](/icons/73673hanshu.gif)
![](/icons/73673de.gif)
过程
![](/icons/73673dou.gif)
其实也就是创建
![](/icons/73673yi.gif)
个对象
![](/icons/73673de.gif)
过程
![](/icons/73673dou2.gif)
每个
![](/icons/73673hanshu.gif)
对象都有
![](/icons/73673yi.gif)
个叫做 [[Scope]]
![](/icons/73673de.gif)
内部属性
![](/icons/73673dou.gif)
这个内部属性包含创建
![](/icons/73673hanshu.gif)
时
![](/icons/73673de.gif)
作用域信息
![](/icons/73673dou2.gif)
实际上
![](/icons/73673dou.gif)
[[Scope]]属性对应
![](/icons/73673de.gif)
是
![](/icons/73673yi.gif)
个对象(Variable Objects)列表
![](/icons/73673dou.gif)
列表中
![](/icons/73673de.gif)
对象是可以从
![](/icons/73673hanshu.gif)
内部访问
![](/icons/73673de.gif)
![](/icons/73673dou2.gif)
比如说我们建立
![](/icons/73673yi.gif)
个全局
![](/icons/73673hanshu.gif)
A
![](/icons/73673dou.gif)
那么A
![](/icons/73673de.gif)
[[Scope]]内部属性中只包含
![](/icons/73673yi.gif)
个全局对象(Global Object)
![](/icons/73673dou.gif)
而如果我们在A中创建
![](/icons/73673yi.gif)
个新
![](/icons/73673de.gif)
![](/icons/73673hanshu.gif)
B
![](/icons/73673dou.gif)
那么B
![](/icons/73673de.gif)
[[Scope]]属性中就包含两个对象
![](/icons/73673dou.gif)
![](/icons/73673hanshu.gif)
A
![](/icons/73673de.gif)
Activation Object对象在前面
![](/icons/73673dou.gif)
全局对象(Global Object)排在后面
![](/icons/73673dou2.gif)
当
![](/icons/73673yi.gif)
个
![](/icons/73673hanshu.gif)
被执行
![](/icons/73673de.gif)
时候
![](/icons/73673dou.gif)
会自动创建
![](/icons/73673yi.gif)
个可以执行
![](/icons/73673de.gif)
对象(Execution Object)
![](/icons/73673dou.gif)
并同时绑定
![](/icons/73673yi.gif)
个作用域链(Scope Chain)
![](/icons/73673dou2.gif)
作用域链会通过下面两个步骤来建立
![](/icons/73673dou.gif)
用于进行标识符解析
![](/icons/73673dou2.gif)
1. 首先将
![](/icons/73673hanshu.gif)
对象[[Scope]]内部属性中
![](/icons/73673de.gif)
对象
![](/icons/73673dou.gif)
按顺序复制到作用域链中
![](/icons/73673dou2.gif)
2. 其次
![](/icons/73673dou.gif)
在
![](/icons/73673hanshu.gif)
执行时
![](/icons/73673dou.gif)
会创建
![](/icons/73673yi.gif)
个新
![](/icons/73673de.gif)
Activation Object对象
![](/icons/73673dou.gif)
这个对象中包含了this、参数(arguments)、局部变量(包括命名
![](/icons/73673de.gif)
参数)
![](/icons/73673de.gif)
定义
![](/icons/73673dou.gif)
这个Activation Object对象会被置于作用域链
![](/icons/73673de.gif)
最前面
![](/icons/73673dou2.gif)
在执行JavaScript代码
![](/icons/73673de.gif)
过程中
![](/icons/73673dou.gif)
当遇到
![](/icons/73673yi.gif)
个标识符
![](/icons/73673dou.gif)
就会根据标识符
![](/icons/73673de.gif)
名称
![](/icons/73673dou.gif)
在执行上下文(Execution Context)
![](/icons/73673de.gif)
作用域链中进行搜索
![](/icons/73673dou2.gif)
从作用域链
![](/icons/73673de.gif)
第
![](/icons/73673yi.gif)
个对象(该
![](/icons/73673hanshu.gif)
![](/icons/73673de.gif)
Activation Object对象)开始
![](/icons/73673dou.gif)
如果没有找到
![](/icons/73673dou.gif)
就搜索作用域链中
![](/icons/73673de.gif)
下
![](/icons/73673yi.gif)
个对象
![](/icons/73673dou.gif)
如此往复
![](/icons/73673dou.gif)
直到找到了标识符
![](/icons/73673de.gif)
定义
![](/icons/73673dou2.gif)
如果在搜索完作用域中
![](/icons/73673de.gif)
最后
![](/icons/73673yi.gif)
个对象
![](/icons/73673dou.gif)
也就是全局对象(Global Object)以后也没有找到
![](/icons/73673dou.gif)
则会抛出
![](/icons/73673yi.gif)
个
![](/icons/73673cuowu.gif)
![](/icons/73673dou.gif)
提示用户该变量未定义(un
![](/icons/73673define.gif)
d)
![](/icons/73673dou2.gif)
这是在ECMA-262标准中描述
![](/icons/73673de.gif)
![](/icons/73673hanshu.gif)
执行模型和标识符解析(Ident
![](/icons/73673if.gif)
ier Resolution)
![](/icons/73673de.gif)
过程
![](/icons/73673dou.gif)
事实证明
![](/icons/73673dou.gif)
大部分
![](/icons/73673de.gif)
JavaScript引擎确实也是这样实现
![](/icons/73673de.gif)
![](/icons/73673dou2.gif)
需要注意
![](/icons/73673de.gif)
是
![](/icons/73673dou.gif)
ECMA-262并没有强制要求采用这种结构
![](/icons/73673dou.gif)
只是对这部分功能加以描述而已
![](/icons/73673dou2.gif)
了解标识符解析(Ident
![](/icons/73673if.gif)
ier Resolution)
![](/icons/73673de.gif)
过程以后
![](/icons/73673dou.gif)
我们就能明白为什么局部变量
![](/icons/73673de.gif)
解析速度要比其他作用域
![](/icons/73673de.gif)
变量快
![](/icons/73673dou.gif)
主要是由于搜索过程被大幅缩短了
![](/icons/73673dou2.gif)
但是
![](/icons/73673dou.gif)
具体会快多少呢?为了回答这个问题
![](/icons/73673dou.gif)
我模拟了
![](/icons/73673yi.gif)
系列
![](/icons/73673de.gif)
测试
![](/icons/73673dou.gif)
来测试区别作用域深度中变量
![](/icons/73673de.gif)
性能
![](/icons/73673dou2.gif)
第
![](/icons/73673yi.gif)
个测试是向
![](/icons/73673yi.gif)
个变量中写入
![](/icons/73673yi.gif)
个最简单
![](/icons/73673de.gif)
值(这里使用字面量
![](/icons/73673de.gif)
数值1)
![](/icons/73673dou.gif)
结果如下图显示
![](/icons/73673dou.gif)
很有趣:
从结果中不难看出
![](/icons/73673dou.gif)
当标识符解析
![](/icons/73673de.gif)
过程需要进行深度搜索时
![](/icons/73673dou.gif)
会伴随性能损失
![](/icons/73673dou.gif)
而且性能损失
![](/icons/73673de.gif)
程度会随着标识符深度
![](/icons/73673de.gif)
增加而递增
![](/icons/73673dou2.gif)
意料的中
![](/icons/73673de.gif)
是
![](/icons/73673dou.gif)
Internet Explorer表现
![](/icons/73673de.gif)
是最差
![](/icons/73673de.gif)
(但公平
![](/icons/73673de.gif)
说
![](/icons/73673dou.gif)
IE 8还是有
![](/icons/73673yi.gif)
些改善
![](/icons/73673de.gif)
)
![](/icons/73673dou2.gif)
值得注意
![](/icons/73673de.gif)
是
![](/icons/73673dou.gif)
这里有
![](/icons/73673yi.gif)
些例外情况
![](/icons/73673dou.gif)
Google Chrome和最新
![](/icons/73673de.gif)
WebKit午夜版在访问变量
![](/icons/73673de.gif)
时间保持得很稳定
![](/icons/73673dou.gif)
不会随着作用域深度
![](/icons/73673de.gif)
递增而增长
![](/icons/73673dou2.gif)
当然
![](/icons/73673dou.gif)
这应该归功于它们所使用
![](/icons/73673de.gif)
下
![](/icons/73673yi.gif)
代 JavaScript引擎
![](/icons/73673dou.gif)
V8和SquirrelFish
![](/icons/73673dou2.gif)
这些引擎在执行代码时进行了优化
![](/icons/73673dou.gif)
而且很明显
![](/icons/73673dou.gif)
这些优化使访问变量
![](/icons/73673de.gif)
速度比以往更快
![](/icons/73673dou2.gif)
Opera表现
![](/icons/73673de.gif)
也很不错
![](/icons/73673dou.gif)
比IE、Firefox和当前版本
![](/icons/73673de.gif)
Safari要快
![](/icons/73673de.gif)
多
![](/icons/73673dou.gif)
但比基于V8和Squirrelfish
![](/icons/73673de.gif)
浏览器要慢
![](/icons/73673dou2.gif)
Firefox 3.1 Beta 2
![](/icons/73673de.gif)
表现有点出人意料
![](/icons/73673dou.gif)
对于局部变量执行
![](/icons/73673de.gif)
效率非常高
![](/icons/73673dou.gif)
但随着作用域层数
![](/icons/73673de.gif)
增加
![](/icons/73673dou.gif)
效率便大打折扣
![](/icons/73673dou2.gif)
需要注意
![](/icons/73673de.gif)
是
![](/icons/73673dou.gif)
我这里使用
![](/icons/73673de.gif)
都是默认设置
![](/icons/73673dou.gif)
也就是说 Firefox是没有开启Trace功能
![](/icons/73673de.gif)
![](/icons/73673dou2.gif)
上面
![](/icons/73673de.gif)
结果是通过对变量执行写操作而得出
![](/icons/73673de.gif)
![](/icons/73673dou.gif)
其实我很好奇
![](/icons/73673dou.gif)
读取变量时
![](/icons/73673de.gif)
情况会不会有什么区别
![](/icons/73673dou.gif)
于是接着做了下面
![](/icons/73673de.gif)
测试
![](/icons/73673dou2.gif)
结果发现
![](/icons/73673dou.gif)
读
![](/icons/73673de.gif)
速度要比写
![](/icons/73673de.gif)
速度快
![](/icons/73673yi.gif)
些
![](/icons/73673dou.gif)
但是性能变化
![](/icons/73673de.gif)
趋势是
![](/icons/73673yi.gif)
致
![](/icons/73673de.gif)
和上个测试
![](/icons/73673yi.gif)
样
![](/icons/73673dou.gif)
Internet Explorer和Firefox还是最慢
![](/icons/73673de.gif)
![](/icons/73673dou.gif)
Opera表现了非常抢眼
![](/icons/73673de.gif)
性能
![](/icons/73673dou.gif)
而同样
![](/icons/73673de.gif)
![](/icons/73673dou.gif)
Chrome和最新版本
![](/icons/73673de.gif)
Webkit午夜版显示了和作用域深度无关
![](/icons/73673de.gif)
性能趋势
![](/icons/73673dou.gif)
同样需要注意
![](/icons/73673de.gif)
是
![](/icons/73673dou.gif)
Firefox 3.1 Beta 2
![](/icons/73673de.gif)
变量访问时间还是会伴随着深度出现
![](/icons/73673yi.gif)
个奇怪
![](/icons/73673de.gif)
跳跃
![](/icons/73673dou2.gif)
在测试
![](/icons/73673de.gif)
过程中
![](/icons/73673dou.gif)
我发现
![](/icons/73673yi.gif)
个有趣
![](/icons/73673de.gif)
现象
![](/icons/73673dou.gif)
就是Chrome在访问全局变量
![](/icons/73673de.gif)
时候会有额外
![](/icons/73673de.gif)
性能损失
![](/icons/73673dou2.gif)
访问全局变量
![](/icons/73673de.gif)
时间和作用域层数没有关系
![](/icons/73673dou.gif)
但是会比访问同样层数
![](/icons/73673de.gif)
局部变量
![](/icons/73673de.gif)
时间多出50%
![](/icons/73673dou2.gif)
这两个测试可以给我们带来什么启示呢?首先是验证了那个古老
![](/icons/73673de.gif)
观点
![](/icons/73673dou.gif)
就是要尽可能
![](/icons/73673de.gif)
使用局部变量
![](/icons/73673dou2.gif)
在所有
![](/icons/73673de.gif)
浏览器下
![](/icons/73673dou.gif)
访问局部变量都比访问跨作用域
![](/icons/73673de.gif)
变量要快
![](/icons/73673dou.gif)
当然也包括全局变量
![](/icons/73673dou2.gif)
下面这几点应该是通过这个测试得出
![](/icons/73673de.gif)
经验吧:
* 仔细检查
![](/icons/73673hanshu.gif)
中所有使用
![](/icons/73673de.gif)
变量
![](/icons/73673dou.gif)
如果有
![](/icons/73673yi.gif)
个变量不是当前作用域定义
![](/icons/73673de.gif)
![](/icons/73673dou.gif)
而且使用了不止
![](/icons/73673yi.gif)
次
![](/icons/73673dou.gif)
那么我们就应该把这个变量保存在局部变量中
![](/icons/73673dou.gif)
而使用这个局部变量来进行读写操作
![](/icons/73673dou2.gif)
这样可以帮助我们将作用域外
![](/icons/73673de.gif)
变量
![](/icons/73673de.gif)
搜索深度减少到1.这对全局变量尤为重要
![](/icons/73673dou.gif)
![](/icons/73673yinwei.gif)
全局变量总是被放到作用域链
![](/icons/73673de.gif)
最后位置来搜索
![](/icons/73673dou2.gif)
* 避免使用with语句
![](/icons/73673dou2.gif)
![](/icons/73673yinwei.gif)
它会修改执行上下文(Execution Context)
![](/icons/73673de.gif)
作用域链
![](/icons/73673dou.gif)
在最前面添加
![](/icons/73673yi.gif)
个对象(Variable Object)
![](/icons/73673dou2.gif)
这就意味着在执行with
![](/icons/73673de.gif)
过程中
![](/icons/73673dou.gif)
实际上
![](/icons/73673de.gif)
局部变量都被移到作用域链上
![](/icons/73673de.gif)
第 2个位置
![](/icons/73673dou.gif)
这会带来性能上
![](/icons/73673de.gif)
损失
![](/icons/73673dou2.gif)
* 如果你确定
![](/icons/73673yi.gif)
段代码肯定会抛出异常
![](/icons/73673dou.gif)
那么就要避免使用try-catch
![](/icons/73673dou.gif)
![](/icons/73673yinwei.gif)
catch分支在作用域链上
![](/icons/73673de.gif)
处理思路方法和with是
![](/icons/73673yi.gif)
样
![](/icons/73673de.gif)
![](/icons/73673dou2.gif)
但try分支
![](/icons/73673de.gif)
代码是没有性能损失
![](/icons/73673de.gif)
![](/icons/73673dou.gif)
所以还是建议用try-catch来捕获那些不可预知
![](/icons/73673de.gif)
![](/icons/73673cuowu.gif)
![](/icons/73673dou2.gif)
如果你想围绕这个话题展开更多
![](/icons/73673de.gif)
讨论
![](/icons/73673dou.gif)
我在上个月
![](/icons/73673de.gif)
Mountain View JavaScript Meetup中曾经发表了
![](/icons/73673yi.gif)
个小演讲
![](/icons/73673dou2.gif)
可以在SlideShare上下载幻灯片
![](/icons/73673dou.gif)
或者观看聚会
![](/icons/73673de.gif)
完整视频
![](/icons/73673dou.gif)
我
![](/icons/73673de.gif)
演讲大概从11分钟左右时开始
![](/icons/73673dou2.gif)
译者笔记
大家如果在阅读本文
![](/icons/73673de.gif)
过程中
![](/icons/73673dou.gif)
有什么疑惑
![](/icons/73673dou.gif)
建议延伸阅读以下两篇文章:
* Richie写
![](/icons/73673de.gif)
![](/icons/73673smhl.gif)
JavaScript对象模型-执行模型
![](/icons/73673smhr.gif)
*
![](/icons/73673smhl.gif)
ECMA-262第 3版
![](/icons/73673smhr.gif)
![](/icons/73673dou.gif)
主要看看第十章
![](/icons/73673dou.gif)
就是执行上下文(Execution Context)那张
![](/icons/73673dou.gif)
本文提到
![](/icons/73673de.gif)
名词在那里都有详细
![](/icons/73673de.gif)
解释
![](/icons/73673dou2.gif)
在最后
![](/icons/73673de.gif)
时候
![](/icons/73673dou.gif)
Nicholas提到
![](/icons/73673yi.gif)
个Mountain View JavaScript Meetup
![](/icons/73673dou.gif)
Meetup那个网站WebSite其实就是
![](/icons/73673yi.gif)
个各种现实世界活动
![](/icons/73673de.gif)
组织网站WebSite
![](/icons/73673dou.gif)
需要翻墙才能访问
![](/icons/73673dou.gif)
住在Cal
![](/icons/73673if.gif)
ornia真幸福
![](/icons/73673dou.gif)
有那么多
![](/icons/73673de.gif)
好活动可以参加
![](/icons/73673dou.gif)
呵呵