面试中让人迷惑的问题: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中,每当一个函数被调用时,它的执行上下文会被压入调用栈顶;当函数执行完毕后,其执行上下文会被从栈顶弹出,控制权返回到上一层函数或全局作用域。这一过程确保了函数间的正确执行顺序和作用域隔离。

感谢观看!

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

相关推荐
石小石Orz几秒前
Three.js + AI:AI 算法生成 3D 萤火虫飞舞效果~
javascript·人工智能·算法
小行星1253 分钟前
前端预览pdf文件流
前端·javascript·vue.js
join84 分钟前
解决vue-pdf的签章不显示问题
javascript·vue.js·pdf
小行星12510 分钟前
前端把dom页面转为pdf文件下载和弹窗预览
前端·javascript·vue.js·pdf
土豆湿26 分钟前
拥抱极简主义前端开发:NoCss.js 引领无 CSS 编程潮流
开发语言·javascript·css
J总裁的小芒果35 分钟前
Vue3 el-table 默认选中 传入的数组
前端·javascript·elementui·typescript
Lei_zhen9637 分钟前
记录一次electron-builder报错ENOENT: no such file or directory, rename xxxx的问题
前端·javascript·electron
辣条小哥哥39 分钟前
electron主进程和渲染进程之间的通信
javascript·electron·ecmascript
咖喱鱼蛋39 分钟前
Electron一些概念理解
前端·javascript·electron
yqcoder41 分钟前
Vue3 + Vite + Electron + TS 项目构建
前端·javascript·vue.js