# 预编译

预编译又称为预处理,是做些代码文本的替换工作。是整个编译过程的最先做的工作。

一段js代码的执行需要经过三个步骤,

  1. 分析代码
  2. 预编译
  3. 执行语句

# 预编译表现

  1. 任何变量,如果变量未经申明就赋值,此变量就为全局对象所有
  2. 一切var申明的全局变量全是window的属性

解释一下第一点, 变量a没有使用任何的申明关键字, 所以window具有了属性a

a = 10;
console.log(a); // 10
console.log(window.a); // 10

第二点,只有var申明的全局变量才会变成window属性,支持块级作用域的浏览器,用let或者const申明的没有这个行为

var b = 20;
console.log(b); // 20
console.log(window.b); // 20

let c = 20;
console.log(c); // 20
console.log(window.c); // undefined

看一下下面的例子

function foo() {
  var a = (b = 10);
}
foo();
console.log(b); // 10

执行的顺序是

  1. b = 10
  2. var a = b

b 没有被关键字申明,所以变成了全局对象的属性。

# 预编译详解

  1. 函数声明整体提升
  2. 变量 声明提升

看一段下面的例子

function f(a) {
    console.log(a);
    var a = 10;
    console.log(a);
    function a() {}
    console.log(a);
    var b = function () {}
    console.log(b);
}
f(1);

要看懂上面的输出,还是需要再借助一下变量对象的概念。

变量对象会包括:

  1. 函数的所有形参 (如果是函数上下文)
    • 由名称和对应值组成的一个变量对象的属性被创建
    • 没有实参,属性值设为 undefined
  2. 函数声明
    • 由名称和对应值(函数对象(function-object))组成一个变量对象的属性被创建
    • 如果变量对象已经存在相同名称的属性,则完全替换这个属性
  3. 变量声明
    • 由名称和对应值(undefined)组成一个变量对象的属性被创建;
    • 如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性

预编译步骤

  1. 创建AO对象

    AO = {
    }
    
  2. 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined

    AO = {
    	a: undefined
    	b: undefined
    }
    
  3. 将实参值和形参统一

    AO = {  
    	a: 1 
    	b: undefined
    }
    
  4. 在函数体里面找函数声明,如果变量对象已经存在相同名称的属性,则完全替换这个属性

    AO = {  
    	a: function a(){}
    	b: undefined
    }
    

第一行 console.log(a);我们去AO对象找,输出函数a

第二行 var a = 10,变量声明其实已经提升, 所以这里做的主要就是AO.a = 10。此时的AO变成

AO = {  
	a: 10
	b: undefined
}

第三行 console.log(a);我们去AO对象找,输出 10

第四行 在预编译阶段就已经被提升到顶层了,所以这一句可以看做没有

第五行 console.log(a);我们去AO对象找,输出 10

第六行 解释完, AO变成

AO = {  
	a: 10
	b: function b() {}
}

第七行输出函数b

上次更新: 1/22/2025, 9:39:13 AM