一文彻底搞懂作用域

简介: 一文彻底搞懂作用域

网络异常,图片无法展示
|


「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战


什么是作用域


几乎所有编程语言最基本的功能之一,就是能够储存变量当中的值,并且能在之后对这个值进行访问或修改。事实上,正是这种储存和访问变量的值的能力将状态带给了程序。


若没有了状态这个概念,程序虽然也能够执行一些简单的任务,但它会受到高度限制,做不到非常有趣。


这些问题说明需要一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量。这套规则被称为作用域。


也就是说作用域负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并有一套规则,确定当前执行的代码对这些标识符是否有访问权限。


词法作用域


词法作用域简单来说就是定义在词法阶段的作用域。


那什么是词法阶段呢?


它是代码编译的一个步骤,在这个阶段,编译器会把字符串分解成一个个词法单元。


词法单元就是对于编程语言来说有意义的代码块。例如:


var name = "snail"
复制代码


就会被分解为 varnamesnail


接下来进入语法分析阶段,这些词法单元会被转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树。这个树被称为“抽象语法树”(Abstract Syntax Tree,AST)。


最后是代码生成阶段,AST 会被转换为可执行代码。


所以词法作用域是由写代码时将变量和块作用域写在哪里来决定的。


函数作用域


属于这个函数的全部变量都可以在整个函数的范围内使用及复用,如果有嵌套的子函数,也可以在子函数中使用。这样属于函数的变量存储及访问的规则就是函数作用域。


我们把每一个函数的作用域想象成一个气泡,则内部的子函数的作用域对应的气泡就是当前气泡内的一个小气泡,同时当前函数作用域的气泡也被包含在一个更大的气泡内。


块级作用域


块级作用域,顾名思义就是属于一个代码块的作用域,但是很遗憾,ES6 之前,JavaScript 中其实并没有块级作用域。


console.log('1',i) // undefined
for(var i = 0;i<3;i++){}
console.log('2',i) // 3
复制代码


这里我们在 for 循环中定义变量 i,其实是只想在 for 循环内部的上下文中使用 i,但是在 JavaScript 中,var 声明的变量此时会被绑定到外部作用域中,也就是常说的变量提升,有时候,这会给我们带来一些麻烦。


例如循环陷阱:


var result = [];
for(var i = 0;i<10;i++){
result[i] = function(){
  console.log(i)
}
}
result[0](); // 10
result[1](); // 10
复制代码


例如变量覆盖:


var name = 'snail'
if(true){
  var name = 'running snail'
}
console.log(name) // running snail
复制代码


ES6 之前,变量作用域的基本单元就是函数作用域,所以,当我们需要创建一个块作用域,最普遍的方法除了普通的函数声明之外,就是立即调用函数表达式(IIFE)。例如:


var name = 'snail'
;(function(){
  var name = 'running snail'
  console.log(name) // running snail
})()
console.log(name)   // snail
复制代码


基于这样的一个问题,ES6 中引入了 letconst 声明变量的方式,通过 letconst 声明的变量只在当前代码块内有效。


所以它们实际上为 JavaScript 新增了块级作用域。


console.log('1',i) // ReferenceError: i is not defined
for(let i = 0;i<3;i++){}
复制代码


动态作用域


实际上 JavaScript 并不具有动态作用域,它只有词法作用域。原因如下:


词法作用域和动态作用域的区别是:


词法作用域是在写代码或者说变量定义时确定的;


动态作用域是在运行时确定的;


词法作用域关注函数在何处声明,而动态作用域关注函数在何处调用。


又因为 JavaScript 是需要编译再执行的,所以它属于词法作用域,而非动态作用域。


但是 this 机制很像动态作用域。因为 this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。


作用域链


当一个代码块或者函数嵌套在另一个代码块或者函数中时,就发生了作用域的嵌套。因此,当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止。


我们把这之中的每一个作用域看作一环,作用域之间层层嵌套就形成了一个链,这就是作用域链。


好了,以上就是本文总结的作用域相关知识,你学废了吗? <( ̄︶ ̄)>


如有任何问题或建议,欢迎留言讨论!👏🏻👏🏻👏🏻

相关文章
|
6月前
|
Web App开发 安全 Linux
Linux 比起其他系统的5 个优点和 5 个缺点
对Linux系统感兴趣的朋友,可以点击下方书籍进行学习。
|
7月前
|
安全 网络协议 定位技术
什么是自治系统(AS)?
自治系统(AS,Autonomous System)是由一个独立的组织机构(如大学、企业或互联网服务提供商)所管辖和控制的一组网络集合。 在这个系统内部,所有的路由器都遵循统一的路由策略。你可以把它看作是互联网上的一个独立王国。
719 0
如何将一个硬盘同时克隆到多个目标硬盘?图文教程在这里
DiskGenius支持同时克隆到多个目标硬盘,有效解决数据迁移难题。提供三种克隆方式:复制所有扇区、按文件系统结构原样复制和按文件复制,满足不同需求。操作简单,只需选择源盘与目标盘,设置克隆方式并确认执行。注意,目标磁盘数据将被清除,请提前备份。还支持系统迁移优化,确保克隆后系统可正常启动。
|
12月前
|
存储 安全 Windows
u 盘不显示盘符怎么办?
在日常使用电脑的过程中,u盘是我们常用的存储设备之一。无论是传输文件、备份数据,还是安装系统,u盘都扮演着重要的角色。然而,有时候我们会遇到一个令人头疼的问题:插入u盘后,电脑上却没有显示盘符。这种情况不仅让人感到困惑,还可能影响工作进度。那么,遇到u盘不显示盘符的情况,我们该如何解决呢?接下来,我们将从多个角度分析可能的原因,并提供相应的解决方案。
|
存储 JavaScript 索引
码元和码点
在计算机中,字符存储依赖于其ASCII编码值。JS中,每个字符占用16位(2字节)的空间,称为码元,范围0-65535。当字符所需空间超限时,JS使用两个码元表示,总称码点。处理特殊字符如表情时,需自定义方法以正确识别码点,避免乱码,确保字符串操作准确无误。
391 1
|
前端开发 JavaScript 测试技术
React 模拟测试与 Jest
【10月更文挑战第21天】本文介绍了如何使用 Jest 进行 React 组件的单元测试和模拟测试,涵盖了基础概念、常见问题及解决方案,并提供了实践案例。通过学习本文,你将掌握如何有效地使用 Jest 提高代码质量和稳定性。
400 1
|
IDE Java 编译器
Java:如何确定编译和运行时类路径是否一致
类路径(Classpath)是JVM用于查找类文件的路径列表,对编译和运行Java程序至关重要。编译时通过`javac -classpath`指定,运行时通过`java -classpath`指定。IDE如Eclipse和IntelliJ IDEA也提供界面管理类路径。确保编译和运行时类路径一致,特别是外部库和项目内部类的路径设置。
847 5
|
Unix 数据处理 数据库
UNIX操作系统的主要用途
UNIX操作系统的主要用途
887 5
|
前端开发 搜索推荐 Java
重定向和转发到底有啥区别?看这篇就够了!
在Web开发中,重定向和转发是常见操作,但它们有何不同?本文将详细解析重定向与转发的原理、区别及实际应用,帮助你在开发中更好地选择和使用这两种技术。
2824 0
|
机器学习/深度学习 人工智能 分布式计算
编程语言未来发展趋势探析:简化与标准化、并发与分布式、智能应用新篇章
编程语言未来发展趋势探析:简化与标准化、并发与分布式、智能应用新篇章
410 1