1、JavaScript的执行过程

假如我们有下面一段代码,他在JavaScript中是如何被执行的呢?

  1. var name = "why"
  2. console.log(num1)
  3. var num1 = 20
  4. var num2 = 30
  5. var result = num1 + num2
  6. console.log(result)

1.1初始化全局对象

  • js引擎会在执行代码之前,会在堆内存中创建一个全局对象:Global Object(GO)
    • 该对象 所有的作用域(scope)都可以访问;
    • 里面会包含Date、Array、String、Number、setTimeout、setInterval等等;
    • 其中还有一个window属性指向自己;

image.png

1.2执行上下文栈(调用栈)

  • js引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),它是用于执行代码的调用栈
  • 那么现在它要执行谁呢?执行的是全局的代码块
    • 全局的代码块为了执行会构建一个 Global Execution Context(GEC)
    • GEC会 被放入到ECS中 执行;
  • GEC被放入到ECS中里面包含两部分内容

    • 第一部分:在代码执行前,在parser转成AST的过程中,会将全局定义的变量、函数等加入到GlobalObject中, 但是并不会赋值;
      • ü 这个过程也称之为变量的作用域提升(hoisting)
    • 第二部分:在代码执行中,对变量赋值,或者执行其他的函数;

      1.3GEC被放入到ECS中

      image.png

      1.4 GEC开始执行代码

      image.png

      1.5遇到函数如何执行?

  • 在执行的过程中执行到一个函数时,就会根据函数体创建一个函数执行上下文(Functional Execution Context, 简称FEC),并且压入到EC Stack中。

  • FEC中包含三部分内容:

    • 第一部分:在解析函数成为AST树结构时,会创建一个Activation Object(AO):
      • AO中包含形参、arguments、函数定义和指向函数对象、定义的变量;
    • 第二部分:作用域链:由VO(在函数中就是AO对象)和父级VO组成,查找时会一层层查找;
    • 第三部分:this绑定的值:这个我们后续会详细解析;

      1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/21707377/1638431988135-3dafc581-38ba-42a9-9c5a-ef372d06094b.png#clientId=u6c70d59b-9b9c-4&from=paste&height=104&id=u1d1e9834&margin=%5Bobject%20Object%5D&name=image.png&originHeight=139&originWidth=292&originalType=binary&ratio=1&size=37597&status=done&style=shadow&taskId=uc240540e-5aad-47fb-8312-d192982e1af&width=219)

      1.6 FEC被放入到ECS中

image.png

1.7 FEC开始执行代码

image.png

2、变量环境和记录

其实我们上面的讲解都是基于早期ECMA的版本规范:
image.png
在最新的ECMA的版本规范中,对于一些词汇进行了修改:
image.png
通过上面的变化我们可以知道,在最新的ECMA标准中,我们前面的变量对象VO已经有另外一个称呼了变量环境 VE。

3.作用域提升面试题

image.png

3.1 面试题1

  1. var n = 100
  2. function foo() {
  3. //在函数作用域内没有找到变量n,所以将n=200赋值到全局作用域n中
  4. n = 200
  5. }
  6. foo()
  7. console.log(n) //200

3.2 面试题2

  1. function foo() {
  2. console.log(n) //undefined
  3. var n = 200
  4. console.log(n) // 200
  5. }
  6. var n = 100
  7. foo()

3.3 面试题3

  1. var n = 100
  2. function foo1() {
  3. console.log(n); // 2. 100
  4. }
  5. function foo2() {
  6. var n = 200
  7. console.log(n); // 1. 200
  8. foo1()
  9. }
  10. foo2()
  11. console.log(n); // 3. 100

3.4 面试题4

  1. var a = 100
  2. function foo() {
  3. console.log(a) // undefind
  4. return
  5. var a = 200
  6. }
  7. foo()

3.5 面试题5

  1. function foo() {
  2. // 如果再函数中没有定义变量
  3. //js引擎会在全局作用域中自动定义同名变量
  4. m = 100
  5. }
  6. foo()
  7. console.log(m) // 100
  1. function foo() {
  2. var a = b = 10
  3. // => 转成下面的两行代码
  4. // var a = 10
  5. // b = 10
  6. //b会自动在全局作用域中创建
  7. }
  8. foo()
  9. // console.log(a) 会报错
  10. console.log(b) // 10