阅读 510

Javascript中的预编译

前言

写这个的目的是笔者对于js变量提升一直心存疑惑,原谅我这么菜hhhhh

预编译在什么时候发生

预编译分为全局预编译和函数预编译:全局预编译发生在页面加载完成时执行,而函数预编译发生在函数执行的前一刻。

全局预编译的步骤

  1. 创建GO(Global Object,全局执行期上下文,在浏览器中为window)对象;
  2. 寻找var变量声明,并赋值为undefined;
  3. 寻找function函数声明,并赋值为函数体;
  4. 执行代码。

一个小案例

我们先来看一下下面这段代码:

var x = 1,
    y = z = 0;

function add (n) {
  return n = n + 1;
}

y = add(x);
function add (n) {
  return n = n + 3;
}

z = add(x)
复制代码

接下来我们来按照前面的步骤详细分析它的预编译执行过程:
1.创建一个GO对象

GO{
    // 对象内容为空
}
复制代码
  1. 寻找var变量声明,并赋值为undefined
Go{
    x: undefined
    y: undefined
    z: undefined
}
复制代码
  1. 寻找function函数声明,并赋值为函数体
GO{
   x: undefined
   y: undefined
   z: undefined
   add: function add (n) { return n = n + 1; } => function add (n) { return n = n + 3; }
}
复制代码
  1. 按顺序执行代码
var x = 1,
    y = z = 0;

function add (n) {
  return n = n + 1;
} //预编译环节已经进行了变量提升,故执行时不在看这行代码

y = add(x);
function add (n) {
  return n = n + 3;
}//预编译环节已经进行了变量提升,故执行时不在看这行代码,但是这一函数覆盖了前面的add函数

z = add(x)
复制代码

故而我们的GO对象变成了

GO{
   x: 1
   y: 0
   z: 0
   add: function add (n) { return n = n + 3; }
}
复制代码

所以我们可以知道,此时x的值为1,y的值为4,z的值为4

函数预编译的步骤

  1. 创建AO对象,执行期上下文(后面更新关于执行期上下文详解)。
  2. 寻找函数的形参和变量声明,将变量和形参名作为AO对象的属性名,值设定为undefined.
  3. 将形参和实参相统一,即更改形参后的undefined为具体的形参值。
  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);
复制代码

接下来我们来按照前面的步骤详细分析它的预编译执行过程:

  1. 创建AO对象
AO{
    // 空对象
}
复制代码
  1. 寻找函数的形参和变量声明,将变量和形参名作为AO对象的属性名,值设定为undefined.
AO{
    a: undefined,
    b: undefined
}
复制代码
  1. 将形参和实参相统一
AO{
    a: 1,
    b: undefined
}
复制代码
  1. 寻找函数中的函数声明,将函数名作为AO属性名,值为函数体。
AO{
    a: function(){}
    b: undefined
    d: function(){}
}
复制代码

函数开始逐行顺序执行:

function fn(a){
    console.log(a);// 输出functiona(){}
    var a = 123;
    console.log(a);// 输出123
    
    function a(){};//预编译环节已经进行了变量提升,故执行时不在看这行代码
    console.log(a);// 输出123
    
    var b = function(){};//这个是函数表达式不是函数声明,故不能提升,会对AO中的b重新赋值
    console.log(b);//输出function(){}
    
    function d(){};
 }
复制代码

结果在代码块的注释里

一个综合案例

var a = 1;
console.log(a);
function test(a) {
  console.log(a);
  var a = 123;
  console.log(a);
  function a() {}
  console.log(a);
  var b = function() {}
  console.log(b);
  function d() {}
}
var c = function (){
console.log("I at C function");
}
console.log(c);
test(2);
复制代码
  1. 全局预编译
GO{
    a: undefined,
    c: undefined,
    test: function(a) {
        console.log(a);
        var a = 123;
        console.log(a);
        function a() {}
        console.log(a);
        var b = function() {}
        console.log(b);
        function d() {}
    }
}
复制代码
  1. 按顺序执行代码
GO{
    a: 1,
    c: function (){
        console.log("I at C function");
    },
    test: function(a) {
        console.log(a);
        var a = 123;
        console.log(a);
        function a() {}
        console.log(a);
        var b = function() {}
        console.log(b);
        function d() {}
    }
}
复制代码
  1. 执行到test(2),函数开始预编译
  • 3.1
AO{
    a: undefined,
    b: undefined
}
复制代码
  • 3.2
AO{
    a: 2,
    b: undefined
}
复制代码
  • 3.3
AO{
    a: function(){},
    b: undefined,
    d: function(){}
}
复制代码
  1. 执行结果
var a = 1;
console.log(a); // 1
function test(a) {
  console.log(a); // function a() {}
  var a = 123;
  console.log(a); // 123
  function a() {}
  console.log(a); // 123
  var b = function() {}
  console.log(b); // function b() {}
  function d() {}
}
var c = function (){
console.log("I at C function");
}
console.log(c); // function c(){ console.log("I at C function"); }
test(2);
复制代码

总结

以后遇到关于预编译的相关问题,大家就可以按照上面的步骤进行解决了,还有就是如果大家在遇到的代码里有if语句的话,在预编译阶段先无视他就可。希望这篇文章能够帮助到和我一样迷惑的你!