面试中让人迷惑的问题:JavaScript小知识点-预编译

前言

在JavaScript中,理解执行上下文、变量提升以及调用栈的概念对于深入掌握其运行机制至关重要。本文将围绕这三个核心概念进行详细解析,帮助读者更好地理解JavaScript代码的执行流程。

正文

1.声明提升

声明提升是JavaScript中一个独特的特性,它涉及到变量和函数声明在代码执行前被"提升"到其所在作用域的顶部。这分为两个主要部分:

(1) 变量声明提升:

当JavaScript引擎遇到变量声明(使用varletconst关键字)时,它会在当前作用域的顶部"声明"这些变量,但并不会初始化它们。这意味着变量虽然存在,但其值默认为undefined,直到实际赋值语句执行时才改变。

我们来举个栗子

js 复制代码
console.log(a);
var a=1;

若是在其他语言中先输出后声明,例如以上代码,代码则会报错。

但是JavaScript不会,正是因为其声明提升的特性,让以上代码能够正常运行 输出

(2)函数声明提升

与变量不同,使用函数声明语法定义的函数会整个提升至作用域顶部,包括函数名和函数体。这意味着你可以在声明之前调用函数,而不会引发错误。

我们来看一下这样一段代码

js 复制代码
foo()
function foo(){
    var a=1
    console.log(a);
}

在这段代码中,先调用了函数,后才声明了函数

但是在JavaScript引擎中,它先是编译声明部分,后再运行操作,使得上述代码依旧能够执行不报错

这就是预编译中的声明提升!

2.函数中的预编译

每当一个函数被调用时,JavaScript会创建一个新的执行上下文------函数执行上下文,并进行以下步骤:

  1. 创建执行上下文对象(AO, Activation Object) :这是函数执行环境的具体体现,存储了函数执行时所需的信息。
  2. 处理形参和变量声明 :将函数的形参和内部声明的变量作为AO的属性,初始值设为undefined
  3. 实参与形参的绑定:如果函数被调用时传递了实参,这些实参会覆盖AO中对应的形参属性值。
  4. 函数声明的处理:在函数体内部查找函数声明,并将其作为AO的属性,属性值为函数定义。

什么意思呢?我们来看这样一段代码:

js 复制代码
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)

请问这段代码的执行结果是什么

我们可以按照我们上述所说的四步:

  1. 创建执行上下文对象AO
  2. 处理形参和变量声明,AO{a=undefined,b=undefined,d=undefined}
  3. 实参与形参的绑定,AO{a=1,b=undefined,d=undefined}
  4. 函数声明的处理,AO{a= function a(),b=function b(),d=undefined}

最后带着初始值将函数运行,结果为:

再来练习一小题,来看看它的运行结果是什么

js 复制代码
function fn(a,b){
    console.log(a);
    c =0
    var c;
    a=3
    b=2
    console.log(b);
    function b(){}
    console.log(b);
}
fn(1)

答案是

做错的同学再思考一下吧!

3.全局的预编译

全局执行上下文(Global Execution Context)是所有非函数代码执行的环境,其初始化过程包括:

  1. 创建全局执行上下文对象(GO, Global Object) :在浏览器中,这通常是window对象。
  2. 变量声明 :将全局范围内使用var声明的变量(不包括letconst)作为GO的属性,初始值为undefined
  3. 函数声明:全局作用域中的函数声明同样会被提升,函数名作为GO的属性,属性值为函数的定义。

我们接着来看一段代码:

js 复制代码
var global = 100
function fn() {
    console.log(global);
}
fn()

这段函数的结果相信大家都没有疑问,函数体内没有声明global,所以访问全局变量global获取值,100

再来看看这一段代码

js 复制代码
global = 100
function fn(){
    console.log(global);
    global = 200
    console.log(global);
    var global =300
}
fn()
var global;

我们开始按我们的步骤走

  1. 创建全局执行上下文对象BO{}
  2. 变量声明:BO{global=undefined}
  3. 函数声明:BO{global=undefined,function fn()}

接着我们再来函数的预编译:

  1. 创建执行上下文对象AO
  2. 处理形参和变量声明,AO{global=undefined}
  3. 实参与形参的绑定,AO{global=undefined}
  4. 函数声明的处理,AO{global=undefined}

然后开始执行,所以执行结果为:

一定要严格按照步骤走!

4.调用栈

调用栈是一种数据结构,用于跟踪函数的调用顺序和状态。在JavaScript中,每当一个函数被调用时,它的执行上下文会被压入调用栈顶;当函数执行完毕后,其执行上下文会被从栈顶弹出,控制权返回到上一层函数或全局作用域。这一过程确保了函数间的正确执行顺序和作用域隔离。

感谢观看!

求点赞评论收藏! 有问题随时私信博主!

相关推荐
web前端神器29 分钟前
forever启动后端服务,自带日志如何查看与设置
前端·javascript·vue.js
才艺のblog32 分钟前
127还是localhost....?
javascript·https·浏览器特性
杰哥在此41 分钟前
Java面试题:解释跨站脚本攻击(XSS)的原理,并讨论如何防范
java·开发语言·面试·编程·xss
今天是 几 号1 小时前
WEB攻防-XSS跨站&反射型&存储型&DOM型&标签闭合&输入输出&JS代码解析
前端·javascript·xss
进击的阿三姐1 小时前
vue2项目迁移vue3与gogocode的使用
前端·javascript·vue.js
风火一回2 小时前
webpack+webpack server入门
javascript·webpack
nbsaas-boot3 小时前
为什么面向对象的设计方法逐渐减少
前端·javascript·vue.js
小满zs3 小时前
Nodejs 第八十五章(集群)
前端·node.js
binnnngo3 小时前
网页计算器的实现
javascript·css3·html5
这河里吗l3 小时前
Java后端每日面试题(day2)
后端·面试