作用域
定义 : 作用的范围,确定当前执行代码对变量的访问权限
JavaScript采用静态作用域
静态作用域/词法作用域 : 函数作用域在函数定义的时候就已经确定了
动态作用域 : 函数作用域在函数调用时才确定
//静态作用域
var value = 1;
function foo() {
console.log(value);
}
function bar() {
var value = 2;
foo();
}
bar(); //1
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();//local scope
作用域链
定义:当查找变量时,会从当前上下文的变量对象中查找,如果没有找到,就会从父级执行上下文的变量对象中找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象所构成的链表就叫作用域链
定义函数时
函数存在一个内部属性[[scope]],函数被创建时,就会保存所有的父变量对象到其中,不包含本身的变量对象
注意 : [[scope]]不代表完整的作用域链
例子:
function foo() {
function bar() {
...
}
}
//函数创建时的作用域
foo.[[scope]] = [
globalContext.VO
];
bar.[[scope]] = [
fooContext.AO,
globalContext.VO
];
执行函数时
函数会创建执行上下文,激活变量对象生成活动对象AO,再将AO压入到执行上下文的作用域中
作用域创建完整过程演示
var scope = "global scope";
function checkscope(){
var scope2 = 'local scope';
return scope2;
}
checkscope();
1.定义checkscope函数时,保存父级变量对象到内部属性[[scope]]
checkscope.[[scope]] = [
globalContext.VO
];
2.执行checkscope函数,先创建checkscope函数上下文,将checkscope函数上下文压入到上下文栈
ECStack = [
checkscopeContext,
globalContext
];
3.checkscope函数执行上下文的创建阶段
//第一步 : 复制函数[[scope]]的值创建作用域链
checkscopeContext = {
Scope: checkscope.[[scope]],
}
//第二步 : 用arguments创建活动对象,然后初始化活动对象,加入形参、函数声明、变量声明
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope2: undefined
},
Scope: checkscope.[[scope]],
}
//第三步 : 将活动对象压入checkscope作用域顶端
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope2: undefined
},
Scope: [AO, [[Scope]]]
}
4.checkscope函数代码执行阶段,修改AO的属性值
checkscopeContext = {
AO: {
arguments: {
length: 0
},
scope2: 'local scope'
},
Scope: [AO, [[Scope]]]
}
5.查找到scope2的值,返回后函数执行完毕,函数上下文从执行上下文栈的弹出
ECStack = [
globalContext
];