likes
comments
collection
share

小白入门!学习基础之JavaScript中的预编译

作者站长头像
站长
· 阅读数 81

前言

在上一篇文展中我们说到了函数声明会整体提升,而变量声明也会提升,可当二者同时出现时,到底哪一个会在顶部呢?

观察如下代码:

console.log(foo); // 输出: function foo() { return 'hello'; }

function foo() {
  return 'hello';
}

var foo = 'world';

小白入门!学习基础之JavaScript中的预编译 在这个例子中,即使在函数声明之前调用了 `console.log(foo)`,代码依然能够正常运行,并且输出了函数定义。这是因为在代码执行之前,函数声明会被提升到作用域的顶部,使得它在变量声明之上可用。

预编译的概念

预编译指的是JavaScript中的预编译是指在代码执行之前,JavaScript引擎会对代码进行一些准备工作,包括变量提升(hoisting)、函数声明提升、作用域的创建等操作。这个过程被称为预编译(Pre-compilation)或者解析(Parsing)阶段。

预编译发生在函数体内时(四部曲)

(一)创建函数的AO对象(Action Object)

(二)找形参和变量声明,将形参和变量声明作为AO的属性名,值赋予为undefined

(三)将形参和实参统一

(四)在函数体内找函数声明,将函数名作为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(){}
    var d = a
    console.log(d);
}
fn(1)

此时按照预编译的步骤开始编译:

(一)创建函数的AO对象(Action Object)

AO={}

(二)找形参和变量声明,将形参和变量声明作为AO的属性名,值赋予为undefined

AO={
   a:undefined
   b:undefined
   d:undefined
}

(三)将形参和实参统一

AO={
   a:undefined,1
   b:undefined
   d:undefined
}

(四)在函数体内找函数声明,将函数名作为AO对象的属性名,值赋予函数体

AO={
   a:undefined,1,function a() {}
   b:undefined
   d:undefined,function d() {}
}

预编译四个步骤结束后,AO对象的编译工作便结束了,开始执行代码,将console.log()对应的值输出来,并将各个变量的赋值执行上去

第一个console.log(a)输出时,此时a=function a() {}

AO={
   a:undefined,1,function a() {}
   b:undefined
   d:undefined,function d() {}
}

第二个console.log(a)输出时,此时a的值的更新已被执行,则打印出a=123

第三个console.log(a)输出时,同第二个a一样,值已经被更新为123,所以a=123

AO={
   a:undefined,1,function a() {},123
   b:undefined
   d:undefined,function d() {}
}

第四个console.log(b)输出时,b已由undefined被更新为function (){}

AO={
   a:undefined,1,function a() {},123
   b:undefined,function(){}
   d:undefined,function d() {}
}

第五个console.log(d)输出时,d已经由function d(){}更新为a,而此时的a=123,所以d=123

AO={
   a:undefined,1,function a() {},123
   b:undefined,function(){}
   d:undefined,function d() {},a
}
小白入门!学习基础之JavaScript中的预编译

预编译发生在全局时(三部曲)

(一)创建GO对象(GLobal Object)

(二)找变量声明,将变量名作为GO的属性名,值赋予undefined

(三)在全局找函数声明,将函数名作为GO对象的属性名,值赋予函数体

观察如下代码:此段代码中既有全局编译也有函数体编译

global=100
function fu(){
    console.log(global);
    global =200
    console.log(global);
    var global = 300
}
fn()
var global;

开始全局编译(三部曲)

(一)创建GO对象(GLobal Object)

GO{
}

(二)找变量声明,将变量名作为GO的属性名,值赋予undefined

GO{
  global:undefined
}

(三)在全局找函数声明,将函数名作为GO对象的属性名,值赋予函数体

GO{
  global:undefined
  fn:function fn(){}
}

此时全局编译便结束,开始执行代码........global的值被更新为100,执行fn()函数体,叫编译器来编译函数体。

GO{
  global:undefined100
  fn:function fn(){}
}

开始函数体编译(四部曲)

(一)创建函数的AO对象(Action Object)

AO={
}

(二)找形参和变量声明,将形参和变量声明作为AO的属性名,值赋予为undefined

AO={
   global=undefined
}

(三)将形参和实参统一

AO={
   global=undefined  //此时无实参,所以不变
}

(四)在函数体内找函数声明,将函数名作为AO对象的属性名,值赋予函数体

AO={
   global=undefined //无函数声明,所以也不变
}

四部曲结束后,函数体的编译也结束了,现在开始执行函数,第一个console.log先在自己函数体内的上下文执行对象中(AO)找global,得知global=undefined,所以第一个console.log(global)输出undefined,当第二个console.log(global)执行前,global已被更新为200,所以第二个console.log(global)输出的值为200.

GO{  //全局上下文执行对象
  global:undefined100
  fn:function fn(){}
}

AO={ //函数体上下文执行对象
   global=undefined200(在第一个console后,第二个console前更新为200),
          300(最后更新为300
}

总的来说,预编译阶段主要是对变量和函数声明进行提升,使得我们能够更好的理解Javascript这门语言的代码执行顺序与作用域。

以上便是今天学习讲解的所用内容,欢迎大家参与讨论与指点,最后感谢大家的浏览与支持!!!

转载自:https://juejin.cn/post/7362513291795628095
评论
请登录