重学JS基础-预编译

432 阅读2分钟

预编译

1.JS代码的执行步骤

  1. 语法分析: 主要扫描代码有没有语法上的错误(比如少些括号,写了中文符号)
  2. 预编译: 进行变量的声明提升,函数整体提升,函数执行前一刻的准备工作。
  3. 解释执行: 对js代码进行执行,解释一行,执行一行。

2.预编译的前奏

  1. 暗示全局变量:任何变量未经声明就赋值,此变量归全局所有。
  2. 一切全局变量都是window的属性

例如:

var a = 100;
console.log(window.a); //100
if(1){
    a = 10;
}
console.log(window.a); //10

3.预编译

脚本代码块script执行前,系统执行的操作

  1. 创建一个GO对象,即window全局对象
  2. 查找全局变量声明(包括隐式全局变量声明,省略var声明),变量名作全局对象的属性,值为undefined
  3. 查找函数声明,函数名作为全局对象的属性,值为函数引用

例如

console.log(a);     //undefined
console.log(func);  //func
var a = 100;
function func(){
    return 1;
}

注意:

  1. let和const也会有变量提升,但是在声明之前并不能访问到,因为存在一个暂时性死区。
  2. 函数声明出现在 if 等语句中的情况有点复杂,它仍然作用于脚本、模块和函数体级别,在预处理阶段,仍然会产生变量,它不再被提前赋值,所以下面的代码打印undefined。

console.log(foo);
if(true) {
    function foo(){

    }
}
  1. with关键字能改变代码块内的作用域,所以在使用的时候需要格外注意。这里打印2和underfined,因为在foo函数中,a会被变量提升,但是赋值的时候指定了对象o的作用域,所以只改变了o.a的值。

var a = 1;

function foo() {
    var o= {a:3}
    with(o) {
        var a = 2;
    }
    console.log(o.a);
    console.log(a);
}

foo();
  1. class 声明在全局的行为跟 function 和 var 都不一样。在 class 声明之前使用 class 名,会有变量提升,但是会抛错。
console.log(c);
class c{

}

4.函数执行上下文

函数在执行前会先做下面的操作

  1. 创建一个AO对象(执行期上下文)
  2. 找到函数体中定义的变量和函数形参,变量名作为AO的属性名,值为undefined
  3. 将实参和形参统一
  4. 将定义的函数名挂在AO的属性中,值为函数体。

如果有兴趣,可以看一下这一道面试题

a = 100;
function demo(e){
    function e(){}
    arguments[0] = 2;
    console.log(e);
    if(a){
        var b = 123;
        function c(){}
    }
    var c;
    a = 10;
    var a;
    console.log(b);
    f = 123;
    console.log(c);
    console.log(a);
}
var a;
demo(1);
console.log(a);
console.log(f);

打印结果是

2
undefined
undefined
10
100
123

最后

感谢你能看到这里,如果你觉得这篇文章对你有点用的话,不妨点个赞再走呀~