JavaScript变量的作用域介绍

JavaScript变量的作用域介绍

JavaScript 变量的作用域决定了变量在代码中的可访问性。

var 是 JavaScript 中最早用于声明变量的关键字,它函数作用域或全局作用域。

let 关键字,具有块级作用域、全局作用域。

const关键字,具有块级作用域、全局作用域。

var、let 和 const 关键字对比表

特性

var

let

const

作用域

函数作用域或全局作用域

块级作用域、全局作用域

块级作用域、全局作用域

变量提升

提升到作用域顶部,初始化为 undefined

声明前访问会报错,因存在“暂时性死区”(Temporal Dead Zone)

声明前访问会报错,因存在“暂时性死区”(Temporal Dead Zone)

重复声明

合法,后续声明覆盖前一个值

不合法,抛出 SyntaxError

不合法,抛出 SyntaxError

是否可变

可变

可变

不可变(引用不可变,但内容可变)

是否成为全局对象属性

是(在全局上下文中)

下面展开介绍

1. 作用域类型

全局作用域(Global Scope

  • 定义:在函数外声明的变量。
  • 特点
    • 任何地方都可访问,包括函数内部。过度使用全局变量可能导致命名冲突和代码难以维护。
    • 在浏览器环境中,全局变量通常与 window 对象关联。
    • 浏览器环境中,var 声明的全局变量会成为 window 对象(在浏览器环境中)的属性,let 和 const 不会。
  • 注意

避免隐式全局变量,始终显式声明变量。

在非严格模式下,未使用 var、let 或 const 关键字声明的变量会被隐式提升为全局变量。

在严格模式下,未声明的变量会导致 ReferenceError,因此不会创建隐式全局变量。

var:在全局上下文中声明的变量会成为全局对象的属性,具有全局作用域。

let 和 const:在全局上下文中声明的变量是全局变量,但不会成为全局对象的属性; let 或 const 关键字还可以声明块级作用域(见后面)。

  • 全局变量的创建方式: 

1)隐式全局变量:

myGlobalVar = "Hello, World!"; // 隐式全局变量
console.log(myGlobalVar); // 输出 "Hello, World!"
console.log(window.myGlobalVar); // 输出 "Hello, World!",因为它是 window 的属性

2)显式全局变量:

使用window对象可以明确地创建全局变量。

window.myExplicitGlobalVar = "Hello, again!"; // 显式全局变量
console.log(window.myExplicitGlobalVar); // 输出 "Hello, again!"
console.log(myExplicitGlobalVar); // 输出 "Hello, again!",直接访问也可以。

3) 使用 var、let 或 const 关键字声明全局变量

//使用 var 声明全局变量
var globalVar = "Hello";
console.log(globalVar); // 输出: Hello
console.log(window.globalVar); // 输出: Hello,因为它是 window 对象的属性//使用 let 声明全局变量
let globalLetVar = "Hello";
console.log(globalLetVar); // 输出: Hello
console.log(window.globalLetVar); // 输出: undefined(不是 window 的属性)//使用 const 声明全局变量
const globalConstVar = "Hello";
console.log(globalConstVar); // 输出: Hello
console.log(window.globalConstVar); // 输出: undefined(不是 window 的属性)

注意,虽然全局变量在某些情况下是必要的,但为了防止命名冲突和提高代码质量,现代编程实践中通常不鼓励过度依赖全局变量。

函数作用域(Function Scope

  • 定义:在函数内部用var声明的变量和函数参数,作用范围为整个函数。
  • 特点
    • 在函数内任何位置(包括嵌套代码块)可访问。
  • 示例
function func() {if (true) {var innerVar = '内部变量'; // 属于函数作用域}console.log(innerVar); // '内部变量'(正常访问)
}
func();

 此例同时说明,var 声明的变量没有块级作用域,即使在块(如 if、for、while 等)中声明,变量仍然属于包含它的函数或全局作用域。

var 声明的变量会被提升(hoisting)到当前作用域的顶部,但赋值不会被提升。这意味着变量在声明之前可以访问,但值为 undefined。例如:

console.log(hoistedVar); // 输出: undefined
var hoistedVar = 'I am hoisted';

块级作用域(ES6+

  • 定义:由 {} 包围的代码块(如 if、for),使用 let 或 const 声明。
  • 特点
    • 变量仅在块内有效。
    • 避免循环变量泄露等问题。
  • 示例
if (true) {let blockVar = '块内变量';const PI = 3.14;
}
console.log(blockVar); // 报错:blockVar未定义

此例说明,let 和 const 不会被提升,存在“暂时性死区”(Temporal Dead Zone),即声明前访问会报错。

注意

    •      let 和 const 声明的变量仅在 声明它们的代码块内有效。

    •      如果在函数体的最外层(不嵌套在任何代码块中)声明,则作用域为 整个函数体(因为函数体本身是一个块级作用域)。

    •      如果在函数内的嵌套代码块中声明(如 if 内部),则作用域仅限该代码块。

var、let和const的区别

  • var
    • 具有函数作用域(在函数内部声明的变量只在函数内部有效)。
    • 如果在全局作用域中声明,则具有全局作用域。
    • 存在变量提升(hoisting),但未初始化的变量会返回undefined。
    • 可以重复声明同一个变量。
  • let和const
    • 具有块级作用域(在代码块内部声明的变量只在代码块内部有效)。
    • 不会被提升到块的顶部。
    • 不允许重复声明同一个变量。
    • let声明的变量可以重新赋值,而const声明的变量不能重新赋值(具有只读性,且必须在声明时立即赋值,否则报错))。

2.作用域链与闭包

  • 作用域链(Scope Chain:函数在定义时确定作用域链,逐级向上查找变量。
  • 闭包(Closures:函数保留对外部作用域的引用,即使外部函数已执行完毕。
    • 内部函数可以访问外部函数的变量。
    • 外部函数执行完毕后,其作用域不会被销毁,而是被内部函数引用。

作用域链示例

let globalVar = "global";function outerFunction() {let outerVar = "outer";function innerFunction() {let innerVar = "inner";console.log(innerVar); // 输出: innerconsole.log(outerVar); // 输出: outerconsole.log(globalVar); // 输出: global}innerFunction();
}outerFunction();

当访问一个变量时,JavaScript会按照作用域链的顺序查找变量。作用域链是从当前作用域开始,逐级向上查找,直到全局作用域。

闭包示例

function outerFunction() {let outerVar = "I am outer";function innerFunction() {console.log(outerVar); // 访问外部变量}return innerFunction;
}let myClosure = outerFunction();
myClosure(); // 输出: I am outer

闭包是指函数可以访问其外部作用域的变量,即使外部函数已经执行完毕。

附1、严格模式(使用'use strict'

  • 在严格模式下,未声明的变量会导致ReferenceError,从而避免全局变量污染。
  • 严格模式是ES5引入的一种特殊的运行模式,使代码在更严格的条件下运行。
  • 通过在代码开头添加 "use strict" 声明来启用。
  • 可以应用于整个脚本或单个函数。

启用方式

// 整个脚本使用严格模式
"use strict";
// 后续代码...

// 或在函数内使用
function myFunction() {
    "use strict";
    // 函数代码...
}
 

示例:非严格模式和严格模式下未声明变量直接赋值的行为差异。

假设我们有以下代码片段:

function myFunction() {x = 10; // 未声明的变量 xconsole.log(x); // 输出 x 的值
}myFunction();
console.log(x); // 在全局作用域中访问 x

1)非严格模式下的行为
在非严格模式下,如果直接给未声明的变量赋值,JavaScript 会自动将该变量提升为全局变量。
function myFunction() {
    x = 10; // 未声明的变量 x
    console.log(x); // 输出 10
}

myFunction();
console.log(x); // 输出 10
解释:
在myFunction函数中,变量x没有被声明(没有使用var、let或const),但直接赋值为10。
非严格模式下,JavaScript 会自动将x提升为全局变量。因此,x在函数内部和全局作用域中都可以被访问。
这种行为可能导致意外的全局变量污染。


2)严格模式下的行为
在严格模式下,未声明的变量直接赋值会抛出ReferenceError。
'use strict'; // 启用严格模式

function myFunction() {
    x = 10; // 未声明的变量 x
    console.log(x); // 抛出 ReferenceError : x is not defined
}

myFunction();
console.log(x); // 这行代码不会被执行
解释:
在严格模式下,JavaScript 不允许直接给未声明的变量赋值。
如果尝试给未声明的变量赋值,JavaScript 会抛出ReferenceError,提示变量未定义。
这种行为可以防止意外创建全局变量,减少潜在的错误和命名冲突。
 

关于 JavaScript 严格模式的官方文档可见https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode

附2、可变(Mutable)和不可变(Immutable

“可变”(Mutable)和“不可变”(Immutable)是描述变量或数据是否可以被修改的术语。

1)可变(Mutable

如果一个变量或数据结构的内容可以被修改,那么它就是可变的。这意味着你可以直接改变变量的值或数据结构中的某些部分,而不会创建新的对象。

示例

// 可变的变量

let number = 10;

number = 20; // 修改变量的值,number 现在是 20

// 可变的对象

let obj = { name: "Alice" };

obj.name = "Bob"; // 修改对象的属性,obj 现在是 { name: "Bob" }

obj.age = 25; // 添加新的属性,obj 现在是 { name: "Bob", age: 25 }

在上面的例子中:

number 是一个可变的变量,因为它的值可以从 10 改为 20。

obj 是一个可变的对象,因为它的属性可以被修改或添加。

2)不可变(Immutable

如果一个变量或数据结构的内容不能被修改,那么它就是不可变的。这意味着一旦创建,它的值或内容就无法被改变。如果需要“修改”,通常会创建一个新的对象或变量。

示例

// 不可变的变量(使用 const 声明)

const PI = 3.14;

PI = 3.14159; // 抛出 TypeError: Assignment to constant variable.

在上面的例子中:

PI 是一个不可变的变量,因为它被 const 声明,不能被重新赋值。

不可变对象

虽然 JavaScript 中没有原生的不可变对象,但可以通过一些技术手段(如 Object.freeze())使对象的结构和属性不可变。

JavaScript复制

const obj = Object.freeze({ name: "Alice" });

obj.name = "Bob"; // 不会报错,但不会生效,obj 仍然是 { name: "Alice" }

obj.age = 25; // 不会报错,但不会生效,obj 仍然是 { name: "Alice" }

在上面的例子中:

  • obj 是一个不可变的对象,因为通过 Object.freeze() 方法,它的属性无法被修改或添加。

3. 可变与不可变的区别

  • 可变数据
    • 可以直接修改。
    • 修改操作通常会影响原始数据。
    • 在某些情况下可能导致意外的副作用(如在函数中修改传入的对象)。
  • 不可变数据
    • 不能直接修改。
    • 修改操作会创建一个新的对象或数据结构。
    • 更安全,避免了意外修改导致的错误。
    • 常用于函数式编程,因为函数式编程强调“无副作用”的函数。

小结

  • 可变(Mutable:可以被修改。
  • 不可变(Immutable:不能被修改。
  • 在 JavaScript 中,let 声明的变量是可变的,而 const 声明的变量是不可变的(但对象或数组的内容可以被修改)。
  • 不可变性有助于提高代码的安全性和可维护性,尤其是在复杂的应用中。

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

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

相关文章

Microsoft 365 Copilot中使用人数最多的是哪些应用

今天在浏览Microsoft 365 admin center时发现,copilot会自动整理过去30天内所有用户使用copilot的概况: 直接把这个图丢给copilot让它去分析,结果如下: 总用户情况 总用户数在各应用中均为 561 人,说明此次统计的样本…

ue5.2.1 quixel brideg显示asset not available in uAsset format

我从未见过如此傻x的bug,在ue5.2.1上通过内置quixel下载资源显示 asset not available in uAsset format 解决办法:将ue更新到最新版本,通过fab进入商场选择资源后add to my library 点击view in launcher打开epic launcher,就可…

当电脑上有几个python版本Vscode选择特定版本python

查看当前vscode用的python版本命令 Import sys print(sys.version) 修改VSCODE解释器 打开 VSCode。 按下 CtrlShiftP打开命令面板。 输入 Python: Select Interpreter 并选择它。 从弹出的列表中选择你安装的 Python 解释器。如果你有多个 Python 版本(例如…

Vue 中 nextTick 的原理详解

1. 为什么需要 nextTick Vue 采用 异步渲染机制,当响应式数据发生变化时,Vue 并不会立即更新 DOM,而是将这些变化放入一个 队列 中,并在 同一事件循环(Event Loop)中合并相同的修改,最后执行批…

Spring面试题2

1、compareable和compactor区别 定义与包位置:Comparable是一个接口,位于java.lang包,需要类去实现接口;而Compactor是一个外部比较器,位于java.util包 用法:Comparable只需要实现int compareTo(T o) 方法,比较当前对…

DuodooBMS源码解读之 cncw_statement模块

财务应收应付扩展模组用户使用手册 一、模块概述 财务应收应付扩展模组是一个基于 Odoo18 的扩展模块,主要对财务应收应付相关功能进行了修改和增强。该模块增加了多个功能模块,如预收款单模块、费用类别设置模块等,同时对发票、公司、销售…

JUC并发—9.并发安全集合四

大纲 1.并发安全的数组列表CopyOnWriteArrayList 2.并发安全的链表队列ConcurrentLinkedQueue 3.并发编程中的阻塞队列概述 4.JUC的各种阻塞队列介绍 5.LinkedBlockingQueue的具体实现原理 6.基于两个队列实现的集群同步机制 4.JUC的各种阻塞队列介绍 (1)基于数组的阻塞…

vue项目启动时报错:error:0308010C:digital envelope routines::unsupported

此错误与 Node.js 的加密模块有关,特别是在使用 OpenSSL 3.0 及以上版本时。Vue 项目在启动时可能会依赖一些旧的加密算法,而这些算法在 OpenSSL 3.0 中默认被禁用,导致 error:0308010C:digital envelope routines::unsupported 错误。 解决…

ncDLRES:一种基于动态LSTM和ResNet的非编码RNA家族预测新方法

现有的计算方法主要分为两类:第一类是通过学习序列或二级结构的特征来预测ncRNAs家族,另一类是通过同源序列之间的比对来预测ncRNAs家族。在第一类中,一些方法通过学习预测的二级结构特征来预测ncRNAs家族。二级结构预测的不准确性可能会导致…

爱普生 SG-8101CE 可编程晶振在笔记本电脑的应用

在笔记本电脑的精密架构中,每一个微小的元件都如同精密仪器中的齿轮,虽小却对整体性能起着关键作用。如今的笔记本电脑早已不再局限于简单的办公用途,其功能愈发丰富多样。从日常轻松的文字处理、网页浏览,到专业领域中对图形处理…

SPRING10_getBean源码详细解读、流程图

文章目录 ①. getBean方法的入口-DefaultListableBeanFactory②. DefaultListableBeanFactory调用getBean③. 进入到doGetBean方法④. getSingleton三级缓存方法⑤. getSingleton()方法分析⑥. createBean创建对象方法⑦. 对象创建、属性赋值、初始化⑧. getBean最详细流程图 ①…

IDEA中查询Maven项目的依赖树

在Maven项目中,查看项目的依赖树是一个常见的需求,特别是当你需要了解项目中直接或间接依赖了哪些库及其版本时。你可以通过命令行使用Maven的dependency:tree插件来做到这一点。这个命令会列出项目中所有依赖的树状结构。 打开idea项目的终端&#xff…

深入xtquant:财务数据获取与应用的实战指南

深入xtquant:财务数据获取与应用的实战指南 在量化交易领域,虽然技术分析和市场情绪分析占据了主导地位,但财务数据作为评估公司基本面的重要依据,同样不可或缺。xtquant作为一个强大的Python库,提供了便捷的接口来获…

windows 安装 stable diffusion

在windows上安装 stable diffusion,如果windows没有nvidia显卡,想只使用CPU可在webui-user.bat中添加命令 set COMMANDLINE_ARGS--no-half --skip-torch-cuda-test 可正常使用stable diffusion,但速度较慢

Kubernetes控制平面组件:APIServer 基于 引导Token 的认证机制

云原生学习路线导航页(持续更新中) kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计(一)Kubernetes架构原则和对象设计(二)Kubernetes架构原则和对象设计(三)Kubernetes控…

DeepSeek 助力 Vue 开发:打造丝滑的缩略图列表(Thumbnail List)

前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 Deep…

DeepSeek写俄罗斯方块手机小游戏

DeepSeek写俄罗斯方块手机小游戏 提问 根据提的要求,让DeepSeek整理的需求,进行提问,内容如下: 请生成一个包含以下功能的可运行移动端俄罗斯方块H5文件: 核心功能要求 原生JavaScript实现,适配手机屏幕 …

百问网(100ask)的IMX6ULL开发板的以太网控制器(MAC)与物理层(PHY)芯片(LAN8720A)连接的原理图分析(包含各引脚说明以及工作原理)

前言 本博文承接博文 https://blog.csdn.net/wenhao_ir/article/details/145663029 。 本博文和博文 https://blog.csdn.net/wenhao_ir/article/details/145663029 的目录是找出百问网(100ask)的IMX6ULL开发板与NXP官方提供的公板MCIMX6ULL-EVK(imx6ull14x14evk)在以太网硬件…

QT开发技术 【opencv图片裁剪,平均哈希相似度判断,以及获取游戏窗口图片】

一、图片裁剪 int CJSAutoWidget::GetHouseNo(cv::Mat matMap) {cv::imwrite(m_strPath "/Data/map.png", matMap);for (int i 0; i < 4; i){for (int j 0; j < 6; j){// 计算当前子区域的矩形cv::Rect roi(j * 20, i * 17, 20, 17);// 提取子区域cv::Mat …

TiDB 是一个分布式 NewSQL 数据库

TiDB 是一个分布式 NewSQL 数据库。它支持水平弹性扩展、ACID 事务、标准 SQL、MySQL 语法和 MySQL 协议&#xff0c;具有数据强一致的高可用特性&#xff0c;是一个不仅适合 OLTP 场景还适合 OLAP 场景的混合数据库。 TiDB是 PingCAP公司自主设计、研发的开源分布式关系型数据…