JavaScript 中变量的作用域是什么?

2023-12-19

javascript中变量的作用域是什么?它们在函数内部和外部具有相同的作用域吗?或者说这有什么关系吗?另外,如果全局定义变量,它们存储在哪里?


TLDR

JavaScript 具有词法(也称为静态)作用域和闭包。这意味着您可以通过查看源代码来了解标识符的范围。

这四个范围是:

  1. 全局 - 一切可见
  2. 函数 - 在函数(及其子函数和块)内可见
  3. 块 - 在块(及其子块)内可见
  4. 模块 - 在模块内可见

除了全局和模块作用域的特殊情况之外,变量的声明使用var(函数范围),let(块作用域),以及const(块范围)。大多数其他形式的标识符声明在严格模式下具有块作用域。

Overview

范围是标识符有效的代码库区域。

词法环境是标识符名称和与其关联的值之间的映射。

作用域由词法环境的链接嵌套构成,嵌套中的每个级别对应于祖先执行上下文的词法环境。

这些链接的词汇环境形成了范围“链”。标识符解析是沿着这条链搜索匹配标识符的过程。

标识符解析仅发生在一个方向:向外。这样,外部词汇环境就无法“看到”内部词汇环境。

决定的因素有3个scope https://en.wikipedia.org/wiki/Scope_(computer_science) of an 标识符 https://www.ecma-international.org/ecma-262/10.0/index.html#sec-names-and-keywords在 JavaScript 中:

  1. 标识符是如何声明的
  2. 声明标识符的位置
  3. 无论你是在严格模式 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode or 非严格模式 https://developer.mozilla.org/en-US/docs/Glossary/Sloppy_mode

声明标识符的一些方法:

  1. var, let and const
  2. 功能参数
  3. catch 块参数
  4. 函数声明
  5. 命名函数表达式
  6. 全局对象上隐式定义的属性(即,遗漏了var在非严格模式下)
  7. import声明
  8. eval

可以声明一些位置标识符:

  1. 全球背景
  2. 函数体
  3. 普通块
  4. 控制结构的顶部(例如循环、if、while 等)
  5. 控制结构体
  6. Modules

声明样式

var

声明使用的标识符var 有功能范围,除了直接在全局上下文中声明它们时,在这种情况下它们将作为属性添加到全局对象上并具有全局作用域。它们的使用有单独的规则eval功能。

让和常量

声明使用的标识符let and const 具有块作用域,除非它们直接在全局上下文中声明,在这种情况下它们具有全局作用域。

Note: let, const and var 都被吊起 https://stackoverflow.com/a/31222689/38522。这意味着它们定义的逻辑位置是其封闭范围(块或函数)的顶部。但是,使用声明的变量let and const在控制通过源代码中的声明点之前无法读取或分配。过渡期被称为暂时死区。

function f() {
    function g() {
        console.log(x)
    }
    let x = 1
    g()
}
f() // 1 because x is hoisted even though declared with `let`!

函数参数名称

函数参数名称的范围仅限于函数体。请注意,这有一点复杂。声明为默认参数的函数关闭参数表 https://stackoverflow.com/questions/61208843/where-are-arguments-positioned-in-the-lexical-environment/,而不是函数体。

函数声明

函数声明在严格模式下具有块作用域,在非严格模式下具有函数作用域。注意:非严格模式是一组复杂的紧急规则,基于不同浏览器的古怪历史实现。

命名函数表达式

命名函数表达式的范围仅限于其自身(例如,出于递归的目的)。

全局对象上隐式定义的属性

在非严格模式下,全局对象上隐式定义的属性具有全局作用域,因为全局对象位于作用域链的顶部。在严格模式下,这些是不允许的。

eval

In eval字符串,使用声明的变量var将被放置在当前范围内,或者,如果eval间接使用,作为全局对象的属性。

Examples

下面将抛出一个 ReferenceError 因为名称x, y, and z在函数之外没有任何意义f.

function f() {
    var x = 1
    let y = 1
    const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)

以下将抛出一个 ReferenceError :y and z,但不适合x,因为可见度x不受块的限制。定义控制结构体的块,例如if, for, and while,表现类似。

{
    var x = 1
    let y = 1
    const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope

在下面的,x在循环外部可见,因为var具有功能范围:

for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)

...由于这种行为,您需要小心关闭使用声明的变量var循环中。变量只有一个实例x在这里声明,并且它逻辑上位于循环之外。

以下打印5,五次,然后打印5第六次console.log循环外:

for(var x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop

以下打印undefined因为x是块作用域的。回调是一对一异步运行的。新行为let变量意味着每个匿名函数都封闭在一个名为的不同变量上x(不像它会做的那样var),等等整数0通过4已打印:

for(let x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined

以下不会抛出ReferenceError因为可见度x不受块的约束;但是,它会打印undefined因为变量尚未初始化(因为if陈述)。

if(false) {
    var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised

在 a 顶部声明的变量for循环使用let作用域为循环体:

for(let x = 0; x < 10; ++x) {} 
console.log(typeof x) // undefined, because `x` is block-scoped

下面将抛出一个ReferenceError因为可见度x受块约束:

if(false) {
    let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped

声明的变量使用var, let or const全部适用于模块:

// module1.js

var x = 0
export function f() {}

//module2.js

import f from 'module1.js'

console.log(x) // throws ReferenceError

下面将在全局对象上声明一个属性,因为使用声明的变量var在全局上下文中作为属性添加到全局对象中:

var x = 1
console.log(window.hasOwnProperty('x')) // true

let and const在全局上下文中,不向全局对象添加属性,但仍然具有全局作用域:

let x = 1
console.log(window.hasOwnProperty('x')) // false

函数参数可以认为是在函数体中声明:

function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function

Catch 块参数的作用域为 catch 块主体:

try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block

命名函数表达式的作用域仅限于表达式本身:

(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression

在非严格模式下,全局对象上隐式定义的属性具有全局作用域。在严格模式下,您会收到错误。

x = 1 // implicitly defined property on the global object (no "var"!)

console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true

在非严格模式下,函数声明具有函数作用域。在严格模式下,它们具有块作用域。

'use strict'
{
    function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped

它是如何工作的

范围定义为lexical https://stackoverflow.com/a/1047479/38522标识符有效的代码区域。

在 JavaScript 中,每个函数对象都有一个隐藏的[[Environment]]引用是对词汇环境 https://www.ecma-international.org/ecma-262/10.0/index.html#sec-lexical-environments of the 执行上下文 https://www.ecma-international.org/ecma-262/10.0/index.html#sec-execution-contexts(堆栈框架)在其中创建它。

当你调用一个函数时,隐藏的[[Call]]方法被调用。此方法创建一个新的执行上下文,并在新的执行上下文和函数对象的词法环境之间建立链接。它通过复制[[Environment]]函数对象上的值,转换为外部参考 https://www.ecma-international.org/ecma-262/10.0/index.html#sec-lexical-environments新执行上下文的词法环境中的字段。

请注意,新的执行上下文和函数对象的词法环境之间的这种链接称为closure https://stackoverflow.com/a/111114/38522.

因此,在 JavaScript 中,作用域是通过外部引用以“链”形式链接在一起的词法环境来实现的。这个词法环境链称为作用域链,标识符解析通过以下方式发生:沿着链条向上搜索 https://www.ecma-international.org/ecma-262/10.0/index.html#sec-getidentifierreference以获得匹配的标识符。

查出more https://github.com/getify/You-Dont-Know-JS/tree/2nd-ed/scope-closures.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaScript 中变量的作用域是什么? 的相关文章

  • 内联还有用吗? [复制]

    这个问题在这里已经有答案了 我相信 inline已经过时了 因为我读过here https isocpp org wiki faq inline functions 无论您如何将函数指定为inline 这是允许编译器忽略的请求 编译器可能会
  • 是否可以禁用特定 jQuery Ajax 调用的 Turbolinks 以防止页面刷新和滚动?

    我有一个 Rails 5 应用程序 非常想使用 Turbolinks 在应用程序中 有几个 PATCH ajax 调用 它们只是用新数据更新服务器 但不需要担心更新页面的状态 每当这些 ajax 请求返回时 Turbolinks 就会刷新页
  • 我可以从 HTTP 请求中找到无线接入点的 BSSID(MAC 地址)吗?

    假设有人在咖啡店里无线连接到互联网 并向 johnsveryownserver com 发送 HTTP 请求 服务器端 有什么方法可以确定我的MAC地址吗 无线接入点他们连接到什么 请注意 我对他们机器的 MAC 地址不感兴趣 如果我无法使
  • Twisted 的 Deferred 和 JavaScript 中的 Promise 一样吗?

    我开始在一个需要异步编程的项目中使用 Twisted 并且文档非常好 所以我的问题是 Twisted 中的 Deferred 与 Javascript 中的 Promise 相同吗 如果不是 有什么区别 你的问题的答案是Yes and No
  • 在 MongoDB 中查找具有字符串 ID 数组的文档

    我有一个 id 字符串数组 我想将其与 find 函数一起使用 db companies find id in arr arr看起来像这样 563a2c60b511b7ff2c61e938 563a2c60b511b7ff2c61e8b7
  • 使用 Javascript 在 Imacros 中循环

    我如何使用 javascript 循环 imm imacros 脚本 我搜索了一下 发现了这个 for i 0 i lt n i iimPlay marconame iim 但当我使用它时 我的浏览器 Firefox 18 挂起 for i
  • 如何将中间件绑定到socket.io中的事件

    现在您可以将中间件绑定到io use middleware 但这仅在建立套接字连接时触发 有没有办法在将其传递给事件句柄之前拦截它 就像在expressjs中一样 换句话说 In 快递 js你可以做 app get middleware1
  • 将 jquery-mobile 与 Webpack 结合使用

    我正在尝试使用 webpack 加载 jquery mobile 但到目前为止还没有运气 我知道 jquery mobile 依赖于 jquery ui 而 jquery ui 又依赖于 jquery 如何在 Webpack 中设置这样的场
  • 未捕获的引用错误:myFunction 未定义[重复]

    这个问题在这里已经有答案了 这到底是怎么回事 http jsfiddle net sVT54 http jsfiddle net sVT54
  • 将 Sweet Alert 弹出窗口添加到 React 组件中的按钮

    我为 Bootstrap 和 React 找到了这个完美的 Sweet Alert 模块 我在 Meteor 应用程序中使用它 http djorg83 github io react bootstrap sweetalert http d
  • 将音频与视频流合并 Node.js

    我正在创建 YouTube 视频下载器并且正在使用ytdl core库 它无法下载带有音频的高质量视频 因为 youtube 将其放在另一个文件中 但我需要将其全部下载到一个文件中 我已经这样做了 app get download asyn
  • 在 MVC Razor 中的 C# 和 Javascript 之间共享常量

    我想在服务器上的 C 和客户端上的 Javascript 中都使用字符串常量 我将常量封装在 C 类中 namespace MyModel public static class Constants public const string
  • 如何记录返回的事件发射器

    如何记录所发出的事件stream返回于MyFunc 与 JSDoc MyFunc description param Object opts description return Stream description function My
  • AJAX:检查字符串是否为 JSON?

    我的 JavaScript 有时会在这一行崩溃 var json eval this responseText 当争论时会导致崩溃eval 不是 JSON 在进行此调用之前有什么方法可以检查字符串是否为 JSON 我不想使用框架 有什么方法
  • 如何滚动到div内的元素?

    我有一个滚动的div我想在点击它时发生一个事件 它会强制执行此操作div滚动以查看内部元素 我写的JavasCript是这样的 document getElementById chr scrollIntoView true 但这会在滚动时滚
  • Flux + React.js - 操作中的回调是好还是坏?

    让我解释一下我最近遇到的问题 我有 React js Flux 驱动的应用程序 有一个列表显示文章数量 注意 应用程序中有多个不同的列表 和文章详情查看在里面 但每个列表只有一个 API 端点 它返回文章数组 为了显示我需要的详细信息fin
  • 使水平滚动条始终可见,即使底部不在视图中

    我将用一个片段来开始这个问题 该片段几乎显示了我想要完成的任务 wrapper overflow hidden display flex sidebar min width 200px background 333 color FFF co
  • 加载另一个 JS 脚本后加载

    这是我的代码 very big js file lots of html stuff 问题是 这些是异步加载的 有没有办法等待第二个脚本直到第一个脚本加载 如果您使用 jQuery 有一个非常简单的方法可以通过获取脚本 https api
  • ES6 模板文字的延迟执行

    我正在玩新的ES6 模板文字 http tc39wiki calculist org es6 template strings 我首先想到的是String format对于 JavaScript 所以我开始实现一个原型 String pro
  • React Native - 跨屏幕传递数据

    我遇到了一些麻烦react native应用程序 我不知道如何跨屏幕传递数据 我意识到还有其他类似的问题在 SO 上得到了回答 但是这些解决方案对我来说不起作用 我正在使用StackNavigator 这是我的设置App js file e

随机推荐

  • 如何从外部进程将数据写入现有进程的 STDIN?

    我正在寻找将数据写入现有流程的方法STDIN从外部流程 发现类似的问题如何在 Python 中将数据从不同的本地 远程进程流式传输到程序的 STDIN 中 https stackoverflow com questions 3792054
  • 如何在函数中传递命名元组的可选参数

    author mayukhsarkar import collections def search student database None ID None flag False try if ID is None or ID 0 rai
  • 选项 [T] 的最小/最大可能为空序列?

    我正在做一些 Scala 体操Seq T 我试图在其中找到 最小 元素 这就是我现在所做的 val leastOrNone seq reduceOption best current gt if current something lt b
  • SQLiteConstraintException:如何将“无关系”与 Room 中的外键映射

    我使用 Android Room 持久性库作为 ORM 我有以下实体 Entity tableName log entries foreignKeys ForeignKey entity Serving class parentColumn
  • 如何为从方法返回的流启用“类型信息”?

    从几个版本开始 IntelliJ 有一个非常有用的功能 当你将一个单独的方法调用stream 语句在单独的行上 IntelliJ 在每一行上放置类型信息 但当你不打电话时stream 直接 就像从另一个方法返回时一样 该信息被省略 有没有办
  • Visual Studio Designer 总是试图改变我的控制

    我有一个有点复杂的 UserControl 而 Visual Studio 2008 在使用它时给我带来了相当无害的烦恼 每次我用设计器打开控件时 它都会决定立即更改设计器设置的一些无害值 即 Size 属性的初始化 如果我保存这些更改 关
  • 为什么在以下代码中 form.showdialog() 有效而 form.show() 无效

    在下面的代码中 我有一个观察程序 它查看文件是否已更改 如果已更改 我会在表单上显示更改后的信息 但如果我使用 form Show 它会冻结 但 form showDialog 工作正常 这两者有什么区别以及如何确定使用哪一个 privat
  • 在 C 中将 PPM 从 RGB 转换为 HSL [关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我需要有关 RGB 图像文件中的直方图均衡化的帮助来完成我的学术课程 我检查了之前有关直方图均衡的代码示例 但没有找到有关此问题的任何线索
  • 用ggplot填充R中两条黄土平滑线之间的区域

    我想知道如何填充 ggplot 中黄土平滑线之间的区域 以下数据框用于图片 x y ymin ymax grp ydiff 1 1 3 285614 3 285614 10 14177 min 6 8561586 2 1 10 141773
  • 使用 XAML 热重启部署 Xamarin.Forms iOS 应用程序时出错

    我尝试使用 Xamarin Forms 和热重启来开发 iOS 应用程序 但从 Visual Studio 运行该应用程序时不断出现以下错误 尝试部署应用程序 TestApp2 iOS app 时发生错误 详细信息 运行热重启时出错 将应用
  • 使用 python 将列表转换为字符串

    我有包含 int float 和 string 的列表 lists 10 test 10 5 如何将上面的列表转换为字符串 我努力了 val join lists print val 我收到这样的错误 sequence item 0 exp
  • C 职位面试 - 选角和比较

    我遇到了一个棘手的 IMO 问题 我需要比较两个MAC地址 http en wikipedia org wiki MAC address 以最有效的方式 那一刻我脑子里唯一的想法就是一个微不足道的解决方案 一个for循环 比较地点 我就这么
  • 如何在 iOS 应用程序中从蓝牙 LE 设备获取通知

    我正在开发 iOS 蓝牙 LE 应用程序 我能够正确且成功地遵循的功能如下 发现外围设备 连接到外围设备 获取服务和特征 单击读取按钮时能够从特性中读取数据 能够写入数据 在这里我面临一个问题 只有当 BLE 设备将传入数据传输到应用程序时
  • R - 使用 DT 按行格式化

    我有一个数据表 我想在闪亮的应用程序中显示 并按行显示不同的数字格式 我发现用户 NicE 之前提供了一个解决方案 该解决方案在所有列和行都是数字时有效 如下所示 R Shiny DataTables 不按列而是按行格式化数字 https
  • 在相同表名的插入查询中使用选择查询

    是否可以在 插入 查询中使用 选择 查询 但条件是我想对 选择 和 插入 查询使用相同的表名 例如mysql gt insert into sample elements name position ownerel values Names
  • 返回 Oracle 引用游标并附加多个结果

    我有这个问题 希望有人知道答案 我有一个 Oracle 存储过程 它接受客户 ID 并在 ref cursor 中返回所有客户的订单 过于简单化 这就是我所拥有的 Orders orderId siteID Customers siteID
  • 查找 Eclipse 项目中的错误数量

    如何以编程方式查找 Eclipse 项目中的错误数量 以红色标记 主要有两个步骤 您需要访问 Eclipse API 为 Eclipse 编写自己的插件或使用脚本插件 例如时髦的猴子 http groovy codehaus org Gro
  • 在 VIM 中重新打开上次会话缓冲区的替代方案?

    I know mksession 我也用它 但有时 它太多了 它保存了太多状态 这是一个问题 例如 当你更新一些插件 并且想要关闭 打开 VIM 以在新状态下启动它时 除非你只想保留当前打开文件的状态 有时什么 mksession保存与新更
  • 默认的 iPhone 相机应用程序如何能够如此快速地保存照片?

    到目前为止 我已经成功地为 iPhone 创建了一个应用程序 它可以拍摄多张图像 每张图像之间的间隔约为 3 秒 我正在一个单独的线程中异步处理每个图像 一切都很好 直到将图像保存到 iPhone 磁盘上 然后大约需要 12 秒才能使用 J
  • JavaScript 中变量的作用域是什么?

    javascript中变量的作用域是什么 它们在函数内部和外部具有相同的作用域吗 或者说这有什么关系吗 另外 如果全局定义变量 它们存储在哪里 TLDR JavaScript 具有词法 也称为静态 作用域和闭包 这意味着您可以通过查看源代码