【Javascript】作用域
2018-09-21 本文已影响0人
嘻洋洋
定义
当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是
保证对执行环境有权访问的所有变量和函数的有序访问。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即 arguments 对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。
识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直至找到标识符为止。
var color = "blue";
function changeColor(){
var anotherColor = "red";
function swapColors(){
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
// 这里可以访问 color、anotherColor 和 tempColor
}
// 这里可以访问 color 和 anotherColor,但不能访问 tempColor
swapColors();
}
// 这里只能访问 color
changeColor();
以上代码包含三个执行环境: 全局环境、 changeColor() 的局部环境和 swapColors() 的局部环境。
- 全局环境中有一个变量 color 和一个函数 changeColor()
- changeColor() 的局部环境中有一个名为 anotherColor 的变量和一个名为 swapColors() 的函数,但它也可以访问全局环境中的变量 color 。
- swapColors() 的局部环境中有一个变量 tempColor ,该变量只能在这个环境中访问到。开始时会先在自己的变量对象中搜索变量和函数名,如果搜索不到则再搜索上一级作用域链。
标识符(变量)解析的性能
一个标识符所在的位置越深,读写速度就越慢(函数中读写局部变量总是最快的,而读写全局变量总是最慢的),全局变量总是存在于执行环境作用域的最末端,因此它是最深的。
最佳实践:如果某个跨作用域的值在函数中被引用一次以上,那么就把它存储到局部变量中。

上面的函数引用了三次document,而document是个全局对象。搜索该对象的过程必须要遍历整个作用域链,直到最后在全局变量对象中找到。这样就产生了更大的性能开销。以下是改进写法:

首先将document对象的引用存储在局部变量doc中,这样就访问全局变量的次数就减少到了一次。由于doc是个局部变量,因此通过它访问document会更快。当然了,就这一个函数你也许感受不到性能上的提升。但可以想象一下一个大型项目中当有几十个或者上百个全部变量被反复访问的时候,那么性能上的提升一定是显著的。
有时候你会看很多开源项目,比如jQuery,requirejs这样写:
(function() {
// ...
})(this);
(function() {
// ...
}).call(this);
(function() {
// ...
})(window);
大意是把全局变量 window 传入匿名函数内缓存起来,避免直接访问。