预编译

75 阅读4分钟

JS两个特性

JS有两个特性,一个是单线程,一个是解释性语言。不同于编译性语言,解释性语言通常理解为不整体编译,由解释器解释一句执行一句。但JS不是直接对着代码解释执行,在解释执行之前,需要进行其他的步骤。

imply global 暗示全局变量

  1. 即任何变量,如果变量未经声明就赋值,此变量就为全局对象所有。
  2. 在游览器环境下一切声明的全局变量,全部是window的属性 var a = 123 === window.a =123

函数作用域中的预编译(AO对象)

介绍

AO对象全称为:activation object (活跃对象/执行期上下文),在函数执行前执行函数预编译,此时会产生一个AO对象,AO对象保存该函数的参数变量。

执行四步骤

  1. 创建AO对象( activation object )即 活跃对象
  2. 找到形参和变量声明,将变量和形参作为AO对象的属性名,值为undefined
  3. 将实参值和形参统一
  4. 在函数里面声明的函数,函数名作为AO对象的属性名,值赋值给函数体。(若参数名和函数名重叠,则函数体值会覆盖参数值)

代码演示

function fn(a) {
    console.log(a)
   var a = 123
    console.log(a)
   function a() {}
    console.log(a)
   var b = function (){}
    console.log(b)
   function d() {}
}
fn(1)

代码分析

  第一步 创建AO对象,就是执行期上下文
  AO {}
  
  第二步 找到形参和变量声明,将变量和形参最为AO对象的属性名,值为undefined
  AO {
    a: undefined  // 这里形参名和变量名有相同的话只需要写一个
    b: undefined
  }
  
  第三步 将实参值和形参统一
  AO {
    a: 1
    b: undefined
  }
  
  第四步 在函数体里面找函数声明,值赋予函数体
  AO {
    a: function a() {}
    b: function() {}
    d: function d() {}
  }

结果

所以接下来我们再看fn函数整体

  function fn(a) {
    console.log(a)   // function a() {}, 在AO对象中获取
   var a = 123
    console.log(a)   // 123
   function a() {}
    console.log(a)   // 123
   var b = function (){}
    console.log(b)   // function() {}
   function d() {}
}
fn(1)

全局作用域中的预编译(GO对象)

介绍

GO对象全称为 global object(全局对象,等同于window),在开始预编译时产生的对象,比AO对象先产生,用于存放全局变量,也称为全局作用域。

预编译三步骤

  1. 创建GO对象,这里的GO对象 === window对象
  2. 将变量声明的变量名当做GO对象的属性名,值为undefinded
  3. 将声明函数的函数名当做GO对象的属性名,值为函数体

代码演示

var a;
console.log(a)   
function test1(a, b) {
    var a;
  console.log(a)  
     a = 4;
  function test2() {
     a = 5;
     var b = 123;
     function a() {};
     console.log(a)  
  }
  return test2()
}

console.log(a)    
var b = 'this B';
console.log(b)  
test1('this A', 'this b')

代码分析

1. 生成GO对象
GO {}

2. 将声明变量添加进GO对象内,值为undefined
GO {
  a: undefined 
  b: undefined 
}
3. 将声明函数添加进GO对象呢,值为函数体
GO {
  a: undefined 
  b: undefined
  test1: function test1() {...}
}
4. 预编译完成,执行代码:输出两个a,此时a的值为:undefined, 输出b,此时b的值为:this B ,

5. 执行 test1() 函数,生成 AO 对象
test1 AO {}

6. 将 test1() 声明的变量添加到AO对象,值为undefined
test1 AO {
  a: undefined
  b: undefined
}

7. 将 test1() 函数中的形参和实参相统一
test1 AO {
  a: this A
  b: this b
}
此处变量a并没有使用关键字,所以变量a会存放在全局作用域中,全局声明了var a; 赋值4

8. 将 test1() 函数中找函数声明的函数名当做AO对象的属性名,值为函数体
test1 AO {
  a: this A
  b: this b
  test2: function test2() {...}
}

9. 执行 test2() 函数,生成 AO 对象
test2 AO {
  b: undefined
}
此处变量a并没有使用关键字,所以变量a会存放在全局作用域中,全局声明了var a; 赋值4 => 5

10. test2() 函数没有形参和实参跳过

11.  将 test2() 函数中找函数声明的函数名当做AO对象的属性名,值为函数体
test2 AO {
  b: undefined
  a: 5
}

结果

var a;
console.log(a)  // undefined

function test1(a, b) {
    var a;
  console.log(a) // this A
     a = 4;
  function test2() {
     a = 5;
     var b = 123;
      function a() {};
      console.log(a)  // 5
   }
   return test2()
}

console.log(a)   // undefined
var b = 'this B';
console.log(b)   // this B
test1('this A', 'this b')