基础:JavaScript的怪癖之一:提升(Hoisting)

news2024/12/27 11:42:01

JavaScript,通常被称为“Web 语言”,是一种多功能且广泛使用的编程语言。它以其怪癖而闻名,其中之一就是 hoisting(提升)。无论你是经验丰富的开发人员还是刚刚开始你的编码之旅,理解提升对于编写干净和高效的 JavaScript 代码至关重要。

在本文中,我们将带您了解 JavaScript 中的提升概念,揭示变量和函数是如何被提升的。到最后,你不仅能掌握提升背后隐藏的机制,还能学会如何利用它为你带来优势。话不多说,让我们一起进入 JavaScript 提升的迷人世界!

什么是提升?

在我们深入研究细节之前,让我们揭开 JavaScript 中提升的神秘面纱。提升是一个后台进程,它在编译阶段将变量和函数声明移动到它们所包含作用域的顶部。这允许你在正式声明它们之前就使用它们。

想象一下,它就像一个魔术师从帽子里拿出一只兔子,兔子就是你的变量或函数,帽子就是 JavaScript 引擎,提升确保魔术师(JavaScript)总是能找到它需要的兔子(变量或函数),无论它被放在代码的什么位置。

变量提升

var 的魔力

在 JavaScript 中,用 var 声明的变量会表现出一种奇怪的提升行为,当你用 var 声明一个变量时,它会被提升到它所属的函数或全局作用域的顶部。考虑以下例子:

function hoistExample() {
  console.log(myVar); // Outputs: undefined
  var myVar = 42;
  console.log(myVar); // Outputs: 42
}
hoistExample();

hoistExample 函数中,我们尝试在声明 myVar 之前记录它的值。令人惊讶的是,第一个 console.log 语句没有抛出错误。相反,它输出了 undefined 。这是由于提升 —— myVar 的声明被移动到函数的顶部,使其在整个作用域中可访问。

let 和 const 的混合

var 的行为似乎有悖直觉,而且它经常导致意外的 bug,为了解决这个问题,JavaScript 引入了 letconst ,它们具有不同的提升机制。

function hoistExample() {
  console.log(myVar); // Throws a ReferenceError
  let myVar = 42;
  console.log(myVar);
}
hoistExample();

使用 letconst 时,仍然会发生提升,但是变量在代码中实际声明之前不会初始化。这意味着在声明 myVar 之前尝试访问它会导致 ReferenceError 。这种行为促进了代码的更清晰和更可预测性。

函数声明 vs. 表达式

函数声明提升

就像变量一样,JavaScript 中的函数也会被提升,让我们来探讨一下在提升时函数声明和函数表达式之间的区别。

hoistMe(); // Outputs: "I'm hoisted!"
function hoistMe() {
  console.log("I'm hoisted!");
}

在这个例子中,我们在声明 hoistMe 函数之前调用它。由于提升,没有错误,函数按预期执行。函数声明被整体提升,使它们在作用域内的任何地方可用。

函数表达式

另一方面,函数表达式有不同的提升行为。

hoistMe(); // Throws a TypeError
var hoistMe = function () {
  console.log("I'm not hoisted!");
};

在这种情况下,当我们试图在 hoistMe 的声明之前调用它时,我们遇到了一个 TypeError 。函数表达式不会以与函数声明相同的方式被提升。变量 hoistMe 被提升了,但它对函数的赋值没有,这就是为什么在赋值之前调用它会导致错误。

作用域和提升

要完全理解提升,必须掌握 JavaScript 中的作用域的概念,作用域决定了变量和函数在代码中的访问位置。

全局作用域

在任何函数或代码块之外声明的变量具有全局作用域,可以在 JavaScript 代码的任何位置访问它们。

var globalVar = "I'm global!";
function accessGlobalVar() {
  console.log(globalVar); // Outputs: "I'm global!"
}
accessGlobalVar();

在上面的例子中, globalVaraccessGlobalVar 函数中可用,因为它具有全局作用域。

局部作用域

在函数或代码块中声明的变量具有局部作用域,只能在声明它们的作用域中访问它们。

function localScopeExample() {
  var localVar = "I'm local!";
  console.log(localVar); // Outputs: "I'm local!"
}
localScopeExample();
console.log(localVar); // Throws a ReferenceError

localScopeExample 函数中, localVar 具有局部作用域,因此在函数之外无法访问它。尝试全局访问它会导致 ReferenceError

常见的提升陷进

理解提升对于编写干净、无 bug 的代码至关重要。以下是处理提升时需要注意的一些常见陷阱:

重定义变量

当你在同一个作用域中使用 var 多次声明同一个变量时,它不会抛出错误,只是简单地重新赋给变量一个新值。

var myVar = "First declaration";
var myVar = "Second declaration";
console.log(myVar); // Outputs: "Second declaration"

这种行为会导致意想不到的后果,因为重定义变量会使代码更难理解和维护。使用 letconst 来防止意外的变量重声明。

函数重写

在 JavaScript 中,如果你多次声明同一个函数,最后一次声明将覆盖之前的任何一次声明,这可能会导致意想不到的行为和错误。

function myFunction() {
  console.log("First definition");
}
function myFunction() {
  console.log("Second definition");
}
myFunction(); // Outputs: "Second definition"

为了避免函数重写,请始终使用唯一的函数名,并保持清晰和有组织的代码结构。

代码整洁的最佳实践

既然我们已经探讨了提升的细微差别和潜在的陷阱,那么让我们深入研究一些最佳实践,以编写干净和可维护的 JavaScript 代码。

正确声明变量
为了防止与提升相关的问题,在变量的作用域顶部声明变量,如果你使用 var ,考虑切换到 letconst 来利用块作用域,这更可预测,更安全。

function cleanCodeExample() {
  var localVar = "I'm declared at the top";
  //此处显示您的其余代码
}

通过在开头声明变量,可以使代码更具可读性,并减少遇到意外情况的可能性。

组织函数
当使用函数时,请确保定义一致。在代码库中使用函数声明或函数表达式来维护统一的结构。

// 良好做法
function calculateSum(a, b) {
  return a + b;
}

// 避免混淆函数声明和表达式
var multiply = function (a, b) {
  return a * b;
};

代码风格的一致性不仅可以提高代码的清晰度,还可以最大限度地减少与提升相关的问题。

总结:提升的力量

JavaScript 提升是一种隐藏的魔法,它通过将变量和函数声明移动到其作用域的顶部来优化代码,从而增强语言的行为。了解各种变量声明和函数声明的提升,可以让代码更简洁,让专家更熟练。接受并掌握这种方法可以提高代码编写的效率和优雅程度,使其成为开发人员的重要工具。总之,在编码过程中,JavaScript 的怪异之处(包括提升)应作为资产加以利用,以提高工作效率和编码技能。


原文:https://vishwasacharya.hashnode.dev/hoisting-in-javascript

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

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

相关文章

设计模式-状态模式 golang实现

一 什么是有限状态机 有限状态机,英⽂翻译是 Finite State Machine,缩写为 FSM,简称为状态机。 状态机不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。 已订单交易为例: 1.…

绿光集团荣获美业科技创新大奖,杨全军董事长荣获杰出人物

近日,在2023中国(南昌)国际美发美容节之“凤凰之夜,美业盛典”上,香港绿光国际科技集团股份有限公司董事长杨全军先生荣获了2023年度“凤凰”杰出人物奖。同时,绿光集团也因其研发的AI人工智能数字光磁床、…

第21章_InnoDB数据页结构

文章目录 概述UserRecords和FreeSpaceInfimum Supremum(最小记录和最大记录)File Header(文件头部)Page Directory(页目录)File Trailer 概述 它是InnoDB管理存储空间的基本单位,一个页的大小…

【3D图像分割】基于Pytorch的 VNet 3D 图像分割4(改写数据流篇)

在这篇文章:【3D 图像分割】基于 Pytorch 的 VNet 3D 图像分割2(基础数据流篇) 的最后,我们提到了: 在采用vent模型进行3d数据的分割训练任务中,输入大小是16*96*96,这个的裁剪是放到Dataset类…

C++中如何获取虚表和虚函数的地址

获取虚函数的地址 虚函数是C中用于实现多态的一种机制,该机制的原理在此不做赘述。本文主要讨论如何获取虚表以及虚函数的地址? class ClassA { private:int _a;double _b; public:ClassA(int a, double b) : _a(a), _b(b) { }virtual int funcA(int a…

设计模式之组合模式-创建层次化的对象结构

目录 概述概念主要角色应用场景 组合模式的实现类图NS图基本代码组合模式的精髓意外收获(❀❀) 应用示例-公司组织架构管理需求结构图代码 组合模式的优缺点优点缺点 总结 概述 概念 组合模式是一种结构型设计模式,它允许将对象组合成树形结…

xshell和linux什么关系,其实很简单

如果你是从事网络安全相关的工作人员,那你一定对很多人xshell和linux这两词很熟悉,那么xshell和linux究竟是什么关系呢?今天就让小编给你详细讲讲。 xshell和linux什么关系 一、xshell和linux什么关系 Xsehll是一款在Windows平台上运行的远…

在WSL2中安装多个Ubuntu实例

参考:How to install multiple instances of Ubuntu in WSL2 本文主要内容 第一步:在 WSL2 中安装最新的 Ubuntu第二步:下载适用于 WSL2 的 Ubuntu 压缩包第三步:在 WSL2 中安装第二个 Ubuntu 实例第四步:登录到第二个…

pyspark将数据多次插入表的时候报错

代码 报错信息 py4j.protocol.Py4JJavaError: An error occurred while calling o129.sql. : org.apache.spark.sql.catalyst.parser.ParseException: mismatched input INSERT expecting <EOF>(line 12, pos 0) 原因 插入语句结束后没有加&#xff1b;结尾 把两个&am…

自定义注解+拦截器/AOP切面 实现权限管理

一、通过拦截器实现 1 权限表 为了方便&#xff0c;我直接用的现成的权限表&#xff0c;这是表结构 2 自定义注解 首先&#xff0c;创建一个自定义注解&#xff0c;用于controller层的方法或类上 // Target表示该注解可以用在方法和类上 Target({ElementType.METHOD, Ele…

5.注释和文档

本文讲解 Java 中的注释以及文档 ~ 文章目录 1. 注释1.1 引言1.1.1 何为注释&#xff1f;1.1.2 注释有何用&#xff1f; 1.2 类注释1.2 方法注释 2. 文档 1. 注释 1.1 引言 1.1.1 何为注释&#xff1f; 在我看来&#xff0c;注释无非是对一行或多行代码作的解释罢了。它能让…

Java学习_day08_finalnativeabstract接口

文章目录 final关键字注意 native关键字abstract关键字抽象类定义继承 接口定义实现 final关键字 final关键字表示常量&#xff0c;其值在程序运行期间不会改变。 final用来修饰变量&#xff0c;可以是静态变量&#xff0c;也可以是成员变量&#xff0c;也可以是局部变量&…

连接图书馆wifi无法验证如何解决

我们去图书馆连接wifi&#xff0c;无法验证自己身份&#xff0c;怎么办&#xff1f; 一般是电脑怀疑是不安全&#xff0c;进行了拦截。 我们点击不安全 再点击 与此站点的连接不安全 &#xff0c; 了解详情就可以显示登陆界面了&#xff0c;

点击清空按钮表单数据不能清空

清空选项只能清空角色和状态的数据,无法清空用户名的数据, 经过排查后发现 username这个单词写错了,导致点击清空searchFormRef?.resetFields()不能清空用户名数据

算法之路(一)

&#x1f58a;作者 : D. Star. &#x1f4d8;专栏 :算法小能手 &#x1f606;今日分享 : 如何学习&#xff1f; 在学习的过程中&#xff0c;不仅要知道如何学习&#xff0c;还要知道避免学习的陷阱。1. 睡眠不足&#xff1b;2. 被动学习和重读&#xff1b;3. 强调标记或画线&am…

接口--抽象方法

回答问题&#xff1a; 1.接口是什么&#xff1f; 2.接口中可以包含什么内容&#xff1f; 3.如何定义接口格式&#xff1f; 4.接口定义抽象方法格式&#xff1f; Code //接口是公共规范标准&#xff0c;类似于“模具” //如何定义接口格式&#xff1f;/** public interface 接…

【Qt之QVariant】使用

介绍 QVariant类类似于最常见的Qt数据类型的联合。由于C禁止联合类型包括具有非默认构造函数或析构函数的类型&#xff0c;大多数有趣的Qt类不能在联合中使用。如果没有QVariant&#xff0c;则QObject::property()和数据库操作等将会受到影响。 QVariant对象同时持有一个单一…

View 自定义 - 路径 Path

参考文章 一、概念 用于描述顺序 & 区域&#xff0c;单使用无法产生效果。 图形绘制的本质是先画点再将点连接起来&#xff0c;所以点与点之间是存在一个先后顺序的。图形的方向影响的是&#xff1a;添加图形时确定闭合顺序(各个点的记录顺序)、图形的渲染结果(是判断图形…

ArcGIS进阶:水源涵养功能分级评价操作

首先抛出水源涵养重要性评价的公式&#xff1a;水源涵养量降雨量-蒸散发量-地表径流量&#xff0c;其中地表径流量降雨量*平均地表径流系数 声明&#xff1a;以下数据来源于来自于牛强老师书籍&#xff08;城乡规划GIS技术&#xff09;。 以下给出重要性评价阈值表&#xff1…

『MySQL快速上手』-⑥-表的约束

文章目录 1.空属性2.默认值3.列描述4.zerofill5.主键6.自增长7.唯一键8.外键9.综合案例 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&#xff0c;更好的保证数据的合法性&#xff0c;从业务逻辑角度保证数据的正确性。 1.空…