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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

汽车标定技术(六)--基于模型开发如何生成完整的A2L文件(2)

目录 1. 自定义ASAP2文件 2. asap2userlib.tlc需要修改的部分 3. 标定量观测量地址替换 3.1 由elf文件替换 3.2 由map文件替换 3.3 正则表达式(含asap2post.m修改方法) 4.小结 书接上文汽车标定技术(五)--基于模型开发如何生成完整的A2L文件(1)-C…

时间序列预测中的数据分析->周期性、相关性、滞后性、趋势性、离群值等特性的分析方法

本文介绍 本篇文章给大家介绍的是,当我们在进行有关时间序列相关的工作或者实验时,需要对数据进行的一些数据分析操作(包括周期性、相关性、滞后性、趋势性、离群值等等分析)的方法。在本篇文章中会以实战的形式进行讲解,同时提供运行代码和…

若依 验证码出不来 Fontconfig head is null, check your fonts or fonts configuration

是因为使用的OenJDK不支持awt包下的字体 解决方法: 安装FontConfig组件即可 yum install -y fontconfig

C语言--分段函数--switch语句

如何用switch语句写分段函数呢?⭐️ 首先介绍一下switch语句的语法规则⭐️ switch(整形表达式) {case 常量表达式1; //标签必须唯一语句块1;break;case 常量表达式2; //if(a0),而case中时系统自动加语句块2;break;c…

每天一点python——day65

#每天一点Python——65 #字符串的内容对齐操作类似于word中左对齐、右对齐、居中对齐如图 #例: s1hello,python print(s1.center(20,*))#设置宽度20,填充图是*s1有12个字符,这个字符串的宽度设置为20, 20-128 因为center是居中对齐…

MVCC中的可见性算法

在之前的文章 MVCC详解-CSDN博客中我们已经介绍过了MVCC的原理(read viewundo log),今天来详细的说一下readview的匹配规则(可见性算法) 隔离级别在RC,RR的前提下 Read View是如何保证可见性判断的呢&#…

多篇论文介绍-摘要

论文地址https://arxiv.org/pdf/2301.10051.pdf 目录 01CIEFRNet:面向高速公路的抛洒物检测算法 02改进 YOLOv5 的 PDC 钻头复合片缺损识别 03 基于SimAM注意力机制的DCN-YOLOv5水下目标检测 04 基于改进YOLOv7-tiny 算法的输电线路螺栓缺销检测 ​编辑05 基于改进Y…

Unity | Shader(着色器)和material(材质)的关系

一、前言 在上一篇文章中 【精选】Unity | Shader基础知识(什么是shader)_unity shader_菌菌巧乐兹的博客-CSDN博客 我们讲了什么是shader,今天我们讲一下shder和material的关系 二、在unity中shader的本质 unity中,shader就…

pip无法下载moviepy -无法联网

猜测是无法联网 尝试更新匹配 ——失败 尝试1:从网络下载whl文件——还需要下载相关依赖,过于麻烦 但应该可行 下载地址 https://pypi.tuna.tsinghua.edu.cn/simple/对应的包名/ 可能会出现如下,然后继续挨个找 尝试2:使pip联网…

RabbitMQ的高级特性

目录 数据导入 MQ的常见问题 消息可靠性问题 生产者确认机制 SpringAMQP实现生产者确认 消息持久化 消费者消息确认 失败重试机制 消费者失败消息处理策略 死信交换机 TTL 延时队列 待更 数据导入 资料下载地址:day05MQ高级 MQ的常见问题 消息可靠性…

关于卷积神经网络的池化层(pooling)

了解池化层 池化层又称“下采样层”或“子采样层”,池化层可以大大降低特征的维度,减少计算量,同时可以避免过拟合问题。 顾名思义,最大池化层就是从输入的矩阵中某一范围内,选择最大的元素进行保留;平均池…

ThreadLocal原理以及内存泄露问题

1、ThreadLocal实现原理 1、每个线程中有一个ThreadLocalsMap,这是一个哈希表的结构里面有很多entry(也就是k-v),当我们使用ThreadLocal进行set值的时候,会将这个threadLocal设置为key,然后值设置为value放入ThreadLocalsMap,key为弱引用&am…

Python爬虫实战-批量爬取美女图片网下载图片

大家好,我是python222小锋老师。 近日锋哥又卷了一波Python实战课程-批量爬取美女图片网下载图片,主要是巩固下Python爬虫基础 视频版教程: Python爬虫实战-批量爬取美女图片网下载图片 视频教程_哔哩哔哩_bilibiliPython爬虫实战-批量爬取…

兴达易控232转profinet在搅拌站使用案例配置案例

该搅拌站所采用的是双行星动力搅拌桨混合机,借助兴达易控232转profinet网关(XD-PNR200)与PLC和变频器进行通信,从而实现对变频器的精确控制,大大提升了搅拌过程的稳定性和效率。 这一方案还具备高度的灵活性和可扩展性,使得搅拌站…

HashMap源码分析(一)

存储结构 说明:本次讲解的HashMap是jdk1.8中的实现,其他版本可能有差异 内部是由Node节点数组组成,Node节点之间又由链表或红黑树组成。 图是网上找的,实在不想画 属性介绍 //存储数据的数组,初次使用时初始化&…

CocosCreator让一个物体跟随鼠标移动(两种方式 本地坐标系和世界坐标系)

在 Cocos Creator 3.x 游戏运行时显示的画布大小就是屏幕区域,屏幕坐标是从画布的左下角为原点开始计算 在 Creator 3.x 里,屏幕和 UI 是完全区分开的,用户可以在没有 UI 的情况下点击屏幕获取触点信息。因此,获取屏幕触点&#…

mac M2 anaconda 解决装不了python3.7

今天发现一个很奇怪的问题 但是我一换成 conda create -n DCA python3.8.12就是成功的 这个就很奇怪, 解决如下 https://towardsdatascience.com/how-to-manage-conda-environments-on-an-apple-silicon-m1-mac-1e29cb3bad12 998 conda search pythonconda search python …

半导体(芯片)制造工艺流程简单说

半导体行业是国民经济支柱性行业之一,是信息技术产业的重要组成部分,是支撑经济社会发展和保障国家安全的战略性、基础性和先导性产业,其发展程度是衡量一个国家科技发展水平的核心指标之一,属于国家高度重视和鼓励发展的行业。 …

ant design pro of vue怎么使用阿里iconfont

一 使用离线iconfont 首先需要生成图所有图标对应的js文件。如下图所示,将生成的js代码复制,在项目中创建一个js文件,将代码粘贴进去。这里我将js文件放在了src/assets/iconfont下面 然后,在main.js中引入文件,并进…

Java代码是怎么运行的?

Java代码是怎么运行的? 运行流程 将Java程序转换成Java虚拟机所能识别的指令序列,也称Java字节码。之所以这么取名,是因为Java字节码指令的操作码(opcode)被固定为一个字节。 Java虚拟机可以由硬件实现,…