【前端】JavaScript 变量声明和函数声明的提升机制:深入探讨提升优先级与其行为


在这里插入图片描述

博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳]
本文专栏: 前端

文章目录

  • 💯前言
  • 💯提升(Hoisting)概述
  • 💯提升机制——函数声明 vs 变量声明
  • 💯代码示例:函数与 `var` 的提升
    • 提升后的代码解析
    • 分析
  • 💯同名函数和变量声明的行为
    • 提升过程与执行分析
  • 💯为什么函数声明的提升优先于变量声明?
  • 💯变量声明的不同类型——`var`、`let` 和 `const` 的提升行为
    • `let` 和 `const` 的行为
    • 为什么使用 `let` 和 `const` 更安全
  • 💯提升机制中的常见陷阱与最佳实践
    • 常见陷阱
    • 最佳实践
  • 💯小结


在这里插入图片描述


💯前言

  • JavaScript 编程语言中,提升(hoisting)是一个关键的编译特性,它深刻影响代码的行为,直接影响到变量和函数的可见性和可用性。作为 JavaScript 编译与执行过程的重要组成部分,理解提升机制对于编写正确且高效的代码至关重要。尤其是在编写复杂应用时,这一概念的掌握有助于避免潜在的错误,并确保程序逻辑的明确性和可预测性
    本文旨在对 JavaScript 提升机制进行详细探讨,特别是关于变量声明var)和函数声明提升优先级,以及两者之间的复杂关系。我们将通过深入的分析代码示例,系统揭示提升机制的行为模式及其背后的原理
    JavaScript在这里插入图片描述

💯提升(Hoisting)概述

在这里插入图片描述

提升(Hoisting)JavaScript 编译阶段的行为,它涉及到将代码中的变量声明函数声明提升到所在作用域全局作用域的最顶端。这意味着在代码执行之前,JavaScript 引擎会对代码进行预处理,使得这些声明可以在其实际声明之前访问。通俗地说,JavaScript 在编译时会扫描整个作用域,将变量和函数的声明部分提前处理。

这种提升带来的效果使得在代码的任何位置都能够访问变量和函数,甚至是在它们的实际声明之前,从而形成了**“提前可用”的特性。这种机制为 JavaScript 提供了灵活性**,但也带来了代码可读性调试上的挑战,特别是在同名变量和函数存在时,可能会引入意料之外的行为。因此,理解提升的具体原理及其对代码的影响是开发人员的基本功

在进一步探讨提升机制之前,有必要明确 JavaScript 中三种主要的声明方式varletconst,它们在提升时有着显著的差异。此外,函数声明函数表达式在提升方面也表现出不同的行为模式。本文将聚焦于 var 和函数声明的提升,解析它们的优先级及相互关系


💯提升机制——函数声明 vs 变量声明

在这里插入图片描述
在 JavaScript 中,函数和变量的声明都会被提升,但它们的提升方式和处理规则并不相同。

  • 函数声明:函数声明会在编译阶段被完整提升到作用域的最顶端,包括函数的函数体在内。因此,函数可以在其声明之前调用。这种行为赋予了开发者很大的灵活性,尤其是在编写递归函数或将函数作为回调时,可以确保函数在作用域中的可见性和可调用性。

  • var 变量声明var 声明的提升仅限于声明部分,而赋值部分不会被提升。这意味着在作用域的最顶端,变量会被默认初始化为 undefined,直到代码执行到赋值语句时,变量的值才会被真正更新。因此,var 提升后的行为往往会导致一些令人困惑的未定义状态。

通过下面的代码示例,我们可以更好地理解函数声明与 var 声明的提升效果及其相对优先级。


💯代码示例:函数与 var 的提升

在这里插入图片描述
以下代码展示了涉及函数声明和 var 声明的提升过程,展示了函数优先于变量的提升优先级:

console.log(foo);  // 输出函数 foo 的定义function foo() {console.log("Hello, World!");
}var foo = 10;console.log(foo);  // 输出 10

提升后的代码解析

在这里插入图片描述
在上面的代码中,JavaScript 引擎在编译阶段对代码进行了提升,提升后的代码等效于:

function foo() {console.log("Hello, World!");
}var foo;console.log(foo);  // 输出函数 foo 的定义foo = 10;console.log(foo);  // 输出 10

分析

在这里插入图片描述

  • 在编译阶段,函数声明 function foo() 会被提升到作用域的最顶端,因此在代码执行之前,函数 foo 已经在当前作用域内被定义,并且可以在 console.log(foo) 之前使用。
  • 接下来,var foo 的声明也被提升,但它仅仅是声明变量,而不会覆盖函数声明,因此在编译结束时,foo 仍然是指向函数的引用。
  • 执行代码时,第一次 console.log(foo) 输出的是函数 foo 的定义。
  • 随后,当 foo 被赋值为 10 后,再次调用 console.log(foo),这时输出的就是 10

💯同名函数和变量声明的行为

在这里插入图片描述
当函数声明和 var 变量声明同名时,函数声明会优先于变量声明被提升,且变量声明不会覆盖函数声明。然而,变量的赋值操作会在执行阶段覆盖函数的定义,这可能导致函数无法再被调用。

例如:

function x() {x = 20;
}var x;console.log(x);  // 输出函数 x 的定义x = 10;console.log(x);  // 输出 10x();  // 报错:TypeError: x is not a function

提升过程与执行分析

在这里插入图片描述

  1. 提升阶段

    • 函数 x 的声明会被提升到作用域的最顶端,因此在编译阶段,x 被初始化为一个函数。
    • 紧接着,var x 的声明也被提升,但它不会覆盖已经存在的函数定义,只是做了一个声明。
  2. 执行阶段

    • 第一次 console.log(x) 输出的是函数 x,因为此时 x 的值为函数。
    • x = 10 赋值后,x 的引用被修改为数字 10,因此覆盖了之前的函数定义。
    • 最后,当尝试调用 x() 时,由于 x 现在是一个数字而不是函数,因此抛出 TypeError: x is not a function

💯为什么函数声明的提升优先于变量声明?

在这里插入图片描述

函数声明的提升优先于变量声明的原因与 JavaScript 的执行模型息息相关。在 JavaScript 中,代码的执行分为编译阶段执行阶段。在编译阶段,JavaScript 引擎会进行词法分析,将所有的函数声明完整地提升到作用域的最顶端,确保它们在执行阶段可以被调用。

相比之下,var 声明在编译时也会被提升,但初始化部分会保留在原始位置。因此,在赋值之前,变量的初始值是 undefined

这种机制的设计使得函数在作用域内具有更高的优先级,从而确保开发者可以在代码的任何位置调用函数。这种灵活性对于编写结构化代码至关重要,但同时也要求开发者更小心谨慎,以免变量声明和函数声明的混合使用导致不可预料的行为


💯变量声明的不同类型——varletconst 的提升行为

在这里插入图片描述


letconst 的行为

在这里插入图片描述
var 声明不同,letconst 的提升行为更加严格。虽然 letconst 也会被提升,但它们在赋值之前处于暂时性死区(Temporal Dead Zone, TDZ)。在 TDZ 中访问这些变量会抛出 ReferenceError,这是为了确保变量在实际赋值之前不会被访问,从而避免未定义行为。

例如:

console.log(a);  // 抛出 ReferenceError
let a = 10;

在这个例子中,a 虽然被提升,但由于在赋值前处于 TDZ,因此访问会导致 ReferenceError。这种行为使得 letconst 更加安全,防止在变量未被正确初始化之前对其进行访问。


为什么使用 letconst 更安全

在这里插入图片描述
letconst 相比于 var 提供了更加安全和合理的变量管理方式。首先,letconst 不会被初始化为 undefined,而是要求在赋值后才可以使用,这有效避免了许多常见的未定义错误。其次,TDZ 的存在使得这些变量在声明之前是不可访问的,这可以强制开发者遵循正确的变量声明和赋值顺序,增强代码的可读性和健壮性。


💯提升机制中的常见陷阱与最佳实践

在这里插入图片描述
理解提升机制的工作原理,有助于开发者识别并避免在 JavaScript 中的一些常见陷阱。以下列出了一些常见的陷阱以及相应的最佳实践,以帮助开发者编写更可靠的代码。


常见陷阱

在这里插入图片描述

  1. 变量提升导致的 undefined 错误

    • 使用 var 声明的变量在提升后会被初始化为 undefined,因此在赋值之前访问它们会返回 undefined。这种行为可能导致程序意外地处理未定义的值,产生难以察觉的 bug。
    console.log(b);  // 输出 undefined
    var b = 20;
    
  2. 函数和变量同名时的混淆

    • 当函数和变量同名时,变量的赋值操作会覆盖函数的定义,这导致后续对该函数的调用会失败。
    function example() {console.log("I am a function");
    }var example = 10;example();  // 抛出 TypeError: example is not a function
    
  3. letconst 的暂时性死区(TDZ)

    • 当使用 letconst 声明变量时,变量在实际赋值之前处于 TDZ 中,访问这些变量会导致 ReferenceError,这可能会让开发者感到困惑。
    console.log(c);  // 抛出 ReferenceError
    let c = 30;
    

最佳实践

在这里插入图片描述

  1. 避免使用 var

    • 使用 letconst 来代替 var,从而避免由于变量提升和初始化为 undefined 导致的错误。
  2. 保持函数与变量名称的唯一性

    • 为了避免函数和变量的命名冲突,尽量保持函数和变量具有唯一的名称,以免产生意外的覆盖行为。
  3. 将变量声明放在靠近使用的地方

    • 避免将变量声明分散在代码的不同部分,而是将变量声明靠近其首次使用的位置,以增加代码的可读性和维护性。
  4. 了解提升机制

    • 深入理解 JavaScript 提升机制可以帮助开发者编写更加可预测和稳定的代码,减少因提升导致的未定义行为。
  5. 提前声明并初始化变量

    • 在适当的位置声明并初始化变量,确保变量在使用之前已经被赋予正确的值,以减少提升机制带来的混淆。
  6. 小心使用 letconst 的暂时性死区

    • 在使用 letconst 时,要特别注意它们的 TDZ 行为,确保在变量声明和赋值之后再进行访问。

💯小结

  • 在这里插入图片描述
    JavaScript 中的提升机制是一个重要且复杂的概念,理解函数声明与变量声明的提升优先级对于避免代码中的潜在错误至关重要。提升机制使得函数声明在变量声明之前被提升,而 var 声明的变量只会在作用域中被初始化为 undefined,并在代码执行阶段完成赋值。因此,理解这些细节可以帮助开发者编写出更为稳定、健壮和易于维护的代码。

  • 为了最大程度地减少因提升带来的问题,建议开发者尽量使用 letconst,并确保函数和变量之间没有命名冲突。通过掌握提升机制的详细原理和行为,开发者可以更好地控制代码的执行顺序,编写出更加可靠和高效的 JavaScript 程序。

  • 提升机制是 JavaScript 的独特特性之一,它深刻体现了这门语言的动态性与灵活性。理解并正确应用这一特性,不仅有助于开发者更好地编写代码,也有助于增强团队协作时代码的质量与一致性。通过持续的实践和深入学习,开发者可以熟练掌握提升机制,从而避免潜在的错误,编写出健壮、高效且可维护的 JavaScript 应用程序。


在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/887400.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

fastadmin实现站内通知功能

实现效果如下 application/admin/view/common/header.html <style>#notificationMenu {display: none;position: absolute;top: 40px;right: 0;background: #fff;border-radius: 6px;padding: 10px 0;width: 300px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);z-inde…

Axure RP教程:创建高效用户界面和交互

Axure RP是一款广受好评的软件&#xff0c;专门用于设计精致的用户界面和交互体验。这款软件提供了众多UI控件&#xff0c;并根据它们的用途进行了分类。与此同时&#xff0c;国产的即时设计软件作为Axure的替代品&#xff0c;支持在线协作和直接在浏览器中使用&#xff0c;无需…

2024-11-25 二叉树的定义

一、基本概念 1.二叉树是n(n>0)个结点的有限集合: ① 或者为空二叉树&#xff0c;即n0。 ②或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一棵二叉树。 特点&#xff1a; ①每个结点至多只有两棵子树。 ②左右子树不能颠倒&am…

部署实战(二)--修改jar中的文件并重新打包成jar文件

一.jar文件 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的一种文档格式JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中&#xff0c;多出了一个META-INF/MANIFEST.MF 文件META-INF/MANIFEST.MF 文件在生成 JAR 文件的时候…

对象的大小

文章目录 一、对象大小 一、对象大小 对象是类实例化出来的&#xff0c;让我们分析一下类对象中哪些成员呢&#xff1f; 类实例化出的每个对象&#xff0c;每个都有独立的数据空间&#xff0c;所以对象中肯定包含 成员变量&#xff0c;那么成员函数是否包含呢&#xff1f; 首…

01-go入门

文章目录 Go语言学习1. 简介安装windows安装linux安装编译工具安装-goland 2. 入门2.1 Helloworld注释 2.2 变量初始化打印内存地址变量交换匿名变量作用域局部变量全局变量 2.3 常量iota 2.4 数据类型布尔整数浮点类型复数字符串定义字符串字符串拼接符定义多行字符串 map数据…

数据库的联合查询

数据库的联合查询 简介为什么要使⽤联合查询多表联合查询时MYSQL内部是如何进⾏计算的构造练习案例数据案例&#xff1a;⼀个完整的联合查询的过程 内连接语法⽰例 外连接语法 ⽰例⾃连接应⽤场景示例表连接练习 ⼦查询语法单⾏⼦查询多⾏⼦查询多列⼦查询在from⼦句中使⽤⼦查…

LeetCode-632. Smallest Range Covering Elements from K Lists [C++][Java]

目录 题目描述 解题思路 【C】 【Java】 LeetCode-632. Smallest Range Covering Elements from K Listshttps://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/description/ 题目描述 You have k lists of sorted integers in non-decreasing o…

UI自动化测试中公认最佳的设计模式-POM

一、概念 什么是POM&#xff1f; POM是PageObjectModule&#xff08;页面对象模式&#xff09;的缩写&#xff0c;其目的是为了Web UI测试创建对象库。在这种模式下&#xff0c;应用涉及的每一个页面应该定义为一个单独的类。类中应该包含此页面上的页面元素对象和处理这些元…

Scala文件读写——成绩分析

根据文件解决下例问题 1.读入txt文件&#xff1a;按行读入 import scala.io.Sourceobject Test文件读写_成绩分析 {def main(args: Array[String]): Unit {//1.按行读入val source Source.fromFile("score.txt")val it source.getLines()it.next()//跳过第一行wh…

C# Winform 俄罗斯方块小游戏源码

文章目录 1.设计来源俄罗斯方块小游戏讲解1.1 主界面1.2 游戏界面1.3 游戏结束界面1.4 配置游戏界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcLeigh 文章地址&#xff1a;https:…

前端框架Vue3——响应式数据,v-on,v-show和v-if,v-for,v-bind

Vue的定义为渐进式的JavaScript框架。所谓渐进式&#xff0c;是指其被设计 为可以自底向上逐层应用。我们可以只使用Vue框架中提供的某层的功 能&#xff0c;也可以与其他第三方库整合使用。当然&#xff0c;Vue本身也提供了完整的 工具链&#xff0c;使用其全套功能进行项目的…

实验二 系统响应及系统稳定性

实验目的 &#xff08;1&#xff09;学会运用Matlab 求解离散时间系统的零状态响应&#xff1b; &#xff08;2&#xff09;学会运用Matlab 求解离散时间系统的单位取样响应&#xff1b; &#xff08;3&#xff09;学会运用Matlab 求解离散时间系统的卷积和。 实验原理及实…

.NET Core发布网站报错 HTTP Error 500.31

报错如图&#xff1a; 解决办法&#xff1a; 打开任务管理器》》服务》》找到这仨服务&#xff0c;右键启动即可&#xff0c;如果已经启动了就重启&#xff1a;

麒麟安全增强-kysec

DAC: 自主访问控制是linux下默认的接入控制机制,通过对资源读、写、执行操作,保证系统安全 MAC:安全接入控制机制,由操作系统约束的访问控制,默认情况下,MAC不允许任何访问,用户可以自定义策略规则制定允许什么 ,从而避免很多攻击。 MAC强制访问控制常见的实现方式:…

Otter 安装流程

优质博文&#xff1a;IT-BLOG-CN 一、背景 随着公司的发展&#xff0c;订单库的数据目前已达到千万级别&#xff0c;需要进行分表分库&#xff0c;就需要对数据进行迁移&#xff0c;我们使用了otter&#xff0c;这里简单整理下&#xff0c;otter 的安装过程&#xff0c;希望对…

如何解决Jupyter command `jupyter-contrib` not found.

目录 (base) C:\Users\hello>pip show jupyter_contrib_nbextensions Name: jupyter-contrib-nbextensions Version: 0.7.0 Summary: A collection of Jupyter nbextensions. Home-page: https://github.com/ipython-contrib/jupyter_contrib_nbextensions.git Author: ipyt…

Gitee markdown 使用方法(持续更新)

IPKISS 获取仿真器件的名称 引言正文标题换行第一种------在行末尾手动键入两个空格第二种------额外换行一次&#xff0c;即两行中间留一个空行 缩进与反缩进代码块行内代码添加图片添加超链接 加粗&#xff0c;倾斜&#xff0c;加粗倾斜 引言 有些保密性的文件或者教程&…

Adobe Illustrator 2024 安装教程与下载分享

介绍一下 下载直接看文章末尾 Adobe Illustrator 是一款由Adobe Systems开发的矢量图形编辑软件。它广泛应用于创建和编辑矢量图形、插图、徽标、图标、排版和广告等领域。以下是Adobe Illustrator的一些主要特点和功能&#xff1a; 矢量绘图&#xff1a;Illustrator使用矢量…

golang学习5

为结构体添加方法 异常处理过程