作用域链
作用域链
解析器通过创建变量对象(Variable Object)来执行你的代码,VO 在函数体内也会创造 ,函数体内的 VO 我们称之为活动对象(Active Object)。
举个例子:
var a = 10;
function b (c, d) {
var x = 1;
}
对应的 VO
VO: {
a: 10
b: <function reference>
}
AO: {
c: undefined,
d: undefined
x: 1
}
AO 还额外存储了几个属性:
1. callee 指向自身函数
2. length 表示实参的长度(个数)
3. arguments 的索引(index)
每当函数执行的时候,便会创造一个执行期上下文(Execution Context),每个 EC 都有自己的作用域链(Scope),它的数据结构类似一个数组,当前的 AO 会被推入作用链最前端(with 例外)
作用域查找的规则是先查找当前 AO,然后向上查找 VO,直到最后到了 Global Object,还没有就报 undefined 的错
词法作用域(lexical scope)等同于静态作用域(static scope)。所谓的词法作用域其实是指作用域在词法解析阶段既确定了,不会改变。
我们要知道js是遵循静态作用域的。举个例子:
var foo=1;
function static(){
alert(foo);
}
!function(){
var foo=2;
static();
}();
如果js是动态作用域,那么他应该弹出2。请体会一下两者的区别。
另外,借@贺师俊的话,eval 和 with可以产生动态作用域的效果
静态作用域(词法作用域):
function foo() {
print a; // 输出 2
}
function bar() {
var a = 3;
foo();
}
var a = 2;
bar();
动态作用域:
function foo() {
print a; // 输出 3 而不是2 !
}
function bar() {
var a = 3;
foo();
}
var a = 2;
bar();
词法作用域的函数中遇到既不是形参也不是函数内部定义的局部变量的变量时,去函数定义时的环境中查询。
动态域的函数中遇到既不是形参也不是函数内部定义的局部变量的变量时,到函数调用时的环境中查
假设你现在有一个函数 f (x) = a + x
这个函数是不完整的,比如 f (1) = a + 1 你还差一个问题: a 是多少?
有两个方法回答这个问题
第一种叫“动态作用域”,a的值决定于函数调用时上下文中a的值,比如
a = 1;
v=f(1) ; 这里v为2
动态作用域的问题是,函数每一次调用相同的参数未必返回相同的值,其返回值还取决于上下文的某些值
第二种是“词法作用域”,a的值取决于函数定义时上下文中的值
g (a) = lambda (x) a + x;
f = g(2)
这里函数g返回一个和上面函数f形式一样函数,a在此处为2,那么执行
a = 1;
v=f(1) ;这里v为3
因为f要“记住”自己定义时a的值为2,所以实现时
f (x) = a + x 和 a = 2 被打包在一块,被称为“闭包”,意思是它是完整独立的,仅仅依靠调用时参数求值,不再依赖调用时的上下文
作者:baozii
链接:https://www.zhihu.com/question/21865351/answer/20823147
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。