全局执行上下文

在执行全局代码前将window对象确定为全局执行上下文
为全局数据进行预处理

  • var 定义的全局变量==undefined,添加为window的属性
  • function 声明的全局函数==赋值(fun),添加为window的方法
  • this==赋值window对象

开始执行全局代码

函数执行上下文(局部)

在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟存在于栈中)
对函数进行预处理

  • 形参变量接收实参,添加为函数执行上下文的属性
  • arguments 为实参伪数组,添加为函数执行上下文的属性
  • var定义的局部变量先复制undefined,添加为函数执行上下文的属性
  • function 声明的函数==赋值(fun),添加为函数执行上下文的方法
  • this == 赋值为调用函数的对象

开始执行函数体代码

  1. function fn(a1) {
  2. console.log(a1); // 2
  3. console.log(a2); // undefined
  4. a3(); // 'a3()'
  5. console.log(this); // window
  6. console.log(arguments); // 参数伪数组
  7. var a2 = 1;
  8. function a3() {
  9. console.log('a3()');
  10. }
  11. }
  12. fn(2, 3, 4); // window 调用的fn

执行上下文(栈)

image.png

  • 先生成一个全局执行上下文window,入栈
  • 然后执行bar,将bar执行上下文入栈
  • 执行foo,将foo执行上下文入栈
  • foo执行完毕,出栈
  • bar执行完毕,出栈
  • 最后剩下window在栈底

面试题目

1.

  1. console.log('gb', i);
  2. var i = 1;
  3. foo(1);
  4. function foo(i) {
  5. if (i === 4) {
  6. return;
  7. }
  8. console.log('fb', i);
  9. foo(i + 1);
  10. console.log('fe', i);
  11. }
  12. console.log('ge', i);
  13. gb undefined
  14. fb 1
  15. fb 2
  16. fb 3
  17. fe 3
  18. fe 2
  19. fe 1
  20. ge 1
  21. // 产生了 5 个执行上下文

理解:这里的foo是递归,结束条件是 i==4

  1. 首先创建window执行上下文
  2. 执行函数foo(1),添加foo(1) 入执行栈,输出 fb:1
  3. 执行函数foo(2),添加foo(2) 入执行栈,输出 fb:2
  4. 执行函数foo(3),添加foo(3) 入执行栈,输出 fb:3
  5. 执行函数foo(4),添加foo(4) 入执行栈,满足结束条件 return,foo(4)出栈

foo(3)出栈,输出 fb:3
foo(2)出栈,输出 fb:2
foo(1)出栈,输出 fb:1
==> 产生了 5 个执行上下文

2.

  1. var c = 1;
  2. function c(c) {
  3. console.log(c);
  4. }
  5. c(2); // 报错 c 不是一个 function
  6. // 等效
  7. var c;
  8. function c(c) {
  9. console.log(c);
  10. }
  11. c = 1;
  12. c(2);