执行环境:
// 定义了变量或函数有权访问的其他数据,决定了它们各自的行为
// 每个执行环境都有一个变量对象与之对应,执行环境中所定义的所有变量和函数都保存在变量对象中
// 某个执行环境中的所有代码执行完毕后,该执行环境被销毁,保存在其中的所有变量和函数定义也随之销毁
函数与执行环境:
// 每个函数都有自己的执行环境.当执行流进入一个函数时,函数的环境就会被推入一个环境栈中.
// 在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境.
作用域链:
// 当代码在一个环境中执行时,会创建变量对象的一个作用域链.作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。
// 作用域链的前端,始终都是当前执行的代码(又称为词法作用域)所在环境的变量对象.如果这个环境是函数,则将其活动对象作为变量对象.
// 活动对象在最开始时只包含一个变量,即arguments对象.
// 作用域链中的下一个变量来自包含(外部)环境,而在下一个变量对象来自下一个包含环境.
// 这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象// 参考下面的例子
var color = "blue";
function changeColor() {var anotherColor = "red";function swapColors() {var tempColor = anotherColor;anotherColor = color;color = tempColor;}swapColors();
}
changeColor();
console.log(color);
// 作用域链的结构如下:
// Window
// -- color
// -- changeColor()
// -- anotherColor
// -- swapColors()
// -- tempColor// 一共3个执行环境:全局环境、changeColor()的局部环境和swapColors()的局部环境
// swapColors是changeColor的内部环境,changeColor是全局环境的内部环境
// 通过作用域链,可以从内而外 依次、有序的访问.
下面从最简单的全局作用域开始,深入挖掘各类解析方案从而涵盖JavaScript提供的所有作用域
延长作用域链:
// 1.eval:可以接受一个字符串为参数,并将其中的内容视为好像在书写时就存在于程序中这个位置的代码
function foo(str, a) {eval(str);console.log(a, b);
}
var b = 2;
foo("var b = 3;",1 );// 等价于:
function foo(a) {var b = 3;console.log(a,b,);
}
var b = 2;
foo(1)
// 作用域链
// Window
// - b
// - foo
// - b
// - a// 2.with:通常被当作重复引用同一个对象中的多个属性的快捷方式:
function foo(obj) {with(obj) {a = 2; }
};
var o1 = {a: 3
};
var o2 = {b: 3
};
foo(o1);
console.log(o1.a); // 2foo(o2);
console.log(o2.1); // undefined
console.log(a); // 2,泄露到全局上// o2的作用域,foo(...)的作用域和全局作用域中都没有找到标识符a,因此在执行a = 2时,自动创建了一个全局变量
// ps:尽量不用eval 和 with,会影响执行效率!!!
先来看下面的一段代码:
function foo() {var a = 2;function bar () {console.log( a );}return bar;
}
var baz = foo();
baz(); // 2// 1.函数bar()的词法作用域可以访问foo()的内部作用域,
// 2.在foo()执行后,由于javascript引擎的垃圾回收机制会释放不在使用的内存空间.
// 3.闭包,阻止了foo()的内部作用于的释放,原因在于bar()拥有涵盖foo()内部作用域的闭包,使得foo的内部作用域一直存活,以供bar()一直使用// 注:bar()对foo作用域的引用,该引用叫做闭包(闭包可以使得函数继续访问定义时的词法作用域).
参考 《JavaScript高级程序设计》(第3版)P73~P74
参考《你不知道的JavaScript》(上卷)P14~P57