全局执行上下文关联全局对象(包含变量、指向函数对象的指针)
函数都有自己的函数对象(函数名,函数长度、[[scopes]]的指针(作用域的指针))
函数创建时,就已经生成了作用域链对象
每个执行上下文都要关联自己的变量对象
- 作用域链的构建:在函数执行上下文创建时完成,即函数被调用时构建。
- 作用域链的内容:取决于函数定义时的词法作用域,即函数能够访问哪些变量和函数在函数定义时已经确定。
作用域链虽然在执行上下文创建时构建,但是函数的作用域链取决于函数的定义位置
作用域链的构建和内容
- 作用域链的构建:
- 当函数被调用时,JavaScript 引擎会创建一个执行上下文,并初始化作用域链。
- 作用域链的第一层是当前函数的变量对象,然后是所有父级执行上下文的变量对象,直到全局上下文的变量对象。
- 作用域链的内容:
- 作用域链的内容取决于函数定义时的词法作用域。也就是说,函数在定义时就决定了它可以访问哪些变量和函数,这些信息构成了作用域链的内容。
javascript
var num = 100;
function a() { console.log(num); } // a 的词法作用域是全局作用域
function print() {
var num = 200;
a(); // 调用 a
}
print(); // 调用 print
函数执行阶段
- 调用
print
函数:- 创建
print
函数的执行上下文。 print
函数的变量对象包含num
(200
)。print
函数的作用域链包含print
的变量对象和全局变量对象。this
绑定。
- 创建
- 调用
a
函数:- 创建
a
函数的执行上下文。 a
函数的变量对象(空对象,因为a
没有局部变量)。a
函数的作用域链包含a
的变量对象和全局变量对象。this
绑定
- 创建
javascript
function foo() {
const a = 100;
return inner() {
a++;
console.log(a);
}
}
const f = foo();
- 作用域在函数定义时就已经确定,但它所包含的变量在函数被调用时才被实例化。
- 作用域在函数定义时确定:函数的词法作用域在函数定义时就确定了。函数的作用域链是基于函数定义时的上下文建立的。
- 作用域链在函数调用时使用:函数被调用时,执行上下文创建并使用在函数定义时确定的作用域链来解析变量。
创建执行上下文时(即代码执行时),会关联变量对象、绑定this、确定作用域链(函数对象的作用域链对象直接复制给执行上下文中的作用域链对象);
题1:
javascript
var message = '1';
function foo() {
console.log(message);
var message = '2';
}
foo();
执行栈(栈底→栈顶):全局执行上下文、foo执行上下文;
函数执行过程
- foo函数执行上下文中查找message时,优先从自己关联的AO对象中查找;
- 有message且为undefined;
- 打印undefined;
题2
javascript
var message = 'hello'
function bar() {
console.log(message)
}
var obj = {
bar: function() {
var message = 'world';
bar();
}
}
obj.bar() // 'hello'
作用域链和调用位置无关,与创建位置有关;所以打印全局作用域中的message // hello;