Skip to content
  • 全局执行上下文关联全局对象(包含变量、指向函数对象的指针)

  • 函数都有自己的函数对象(函数名,函数长度、[[scopes]]的指针(作用域的指针))

    函数创建时,就已经生成了作用域链对象

    Untitled.png

    Untitled.png

    Untitled.png

每个执行上下文都要关联自己的变量对象

  • 作用域链的构建:在函数执行上下文创建时完成,即函数被调用时构建。
  • 作用域链的内容:取决于函数定义时的词法作用域,即函数能够访问哪些变量和函数在函数定义时已经确定。

作用域链虽然在执行上下文创建时构建,但是函数的作用域链取决于函数的定义位置

作用域链的构建和内容

  • 作用域链的构建
    • 当函数被调用时,JavaScript 引擎会创建一个执行上下文,并初始化作用域链。
    • 作用域链的第一层是当前函数的变量对象,然后是所有父级执行上下文的变量对象,直到全局上下文的变量对象。
  • 作用域链的内容
    • 作用域链的内容取决于函数定义时的词法作用域。也就是说,函数在定义时就决定了它可以访问哪些变量和函数,这些信息构成了作用域链的内容。
javascript
var num = 100;
function a() { console.log(num); }  // a 的词法作用域是全局作用域
function print() {
  var num = 200;
  a();  // 调用 a
}
print();  // 调用 print

函数执行阶段

  1. 调用 print 函数
    • 创建 print 函数的执行上下文。
    • print 函数的变量对象包含 num200)。
    • print 函数的作用域链包含 print 的变量对象和全局变量对象。
    • this 绑定。
  2. 调用 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;

Released under the MIT License.