什么是作用域?
1、作用域可以理解为一个范围盒子这个盒子决定了哪些变量、对象和函数在特定的区域内可以被访问到。
2、作用域也可以堆叠成层次结构,子作用域可以访问父作用域,反过来不行。
作用域分类:全局作用域、函数作用域、块作用域、模块作用域、脚本作用域、Catch 块作用域、包含块作用域、闭包作用域、Eval作用域
全局作用域(Global)
- 定义:在所有函数之外声明的变量和函数处于全局作用域。在浏览器环境中,全局作用域通常是
window对象,在 Node.js 环境中是global对象。 - 特点:全局变量在整个程序的任何地方都可以被访问和修改,他生命周期贯穿整个程序的执行过程,会一直占用内存直到程序结束,这可能导致命名冲突,并且如果不小心使用过多的全局变量,会使程序难以维护。
函数作用域(Local)
- 定义:当函数被调用时,会创建一个新的函数作用域,在这个作用域内,可以访问函数的参数和在函数内部声明的变量。
- 特点:只有在该函数内部才能访问这些变量和参数。当函数执行完毕后,函数作用域中的局部变量通常会被垃圾回收机制回收,释放内存空间。这有助于管理内存,并且可以在不同的函数中使用相同名称的变量而不会相互干扰。
块作用域(Block)
- 定义:从 ES6 开始引入,由
let和const声明的变量具有块级作用域,由一对花括号{}包裹的代码块所形成的作用域,比如在循环、条件语句的代码块等。 - 特点:
let和const声明的变量只在其所在的块级作用域内有效。这避免了变量提升带来的问题,使变量的作用域更加明确,减少错误的发生。
模块作用域
- 定义:每个模块都有独立作用域,其内部变量、函数和类默认对外不可见,只有通过特定导出语句(如
export)暴露的部分才能被其他模块访问(严格来说,模块作用域是特殊的函数作用域)。 - 特点:有助于实现模块化开发,提高代码的可维护性和可复用性,避免了全局变量命名冲突,同时可以更好地控制模块内部的实现细节不被外部随意访问。
脚本作用域(Script)
- 定义:
<script>标签里定义的变量和函数只在该标签范围内有效。多个<script>标签若无特殊交互方式(如模块系统),它们的作用域是分开的。用 let 或 const 声明的全局变量在 Script 范围,可直接使用但不能通过window.xx访问。 - 特点:多个
<script>标签无特别处理时,不同标签内变量与函数可能相互干扰。具体为,若都用var声明或未用let、const限制作用域的同名变量,后加载的会覆盖前面的;函数同理,后定义的同名函数会覆盖前面的。
Catch 块作用域(Catch Block)
- 定义:
catch语句会生成一个特殊的作用域,当发生错误并被catch块捕获时,在catch语句的括号中声明的错误变量只在这个catch块内部有效。 - 特点:可以在
catch块中处理错误,而不用担心这个错误变量会影响到外部作用域。一旦catch块执行完毕,这个错误变量通常会被垃圾回收。
包含块作用域(With Block)(不推荐使用)
- 定义:
with语句可以将一个对象的属性临时添加到作用域链中。例如with(obj) {... },在这个代码块中,可以直接访问obj的属性而无需通过完整的对象引用。 - 特点:这种作用域可能会导致作用域变得不清晰,并且容易引起错误,因为它模糊了变量的来源,现代 JS 开发中不推荐使用
with语句。
闭包作用域(Closure)
- 定义:当一个函数内部定义了另一个函数时,内部函数可以访问外部函数的变量,形成闭包作用域,内部函数可以记住并访问其外部函数的变量,即使外部函数已经执行完毕。
- 特点:闭包可以让函数保留对其外部环境的引用,这在需要保持一些状态或者实现私有变量等场景中非常有用。但是使用不恰当,可能会导致内存泄漏,因为外部函数的变量可能会一直被闭包引用而无法被垃圾回收。
Eval 作用域(不推荐使用)
- 定义:
eval()函数可执行字符串形式的 JS 代码。它在调用时会形成它的作用域并且执行代码,不过会有安全与可维护性问题。在全局作用域调用eval(),代码就影响全局作用域,在函数作用域调用,代码就影响该函数作用域。 - 特点:由于
eval()的行为难以预测,并且可能会引入安全风险,现代 JS 开发中应尽量避免使用eval()。如果必须使用,要非常小心地处理其作用域和潜在的安全问题。
以上这些都是调试得到的,是 JS 引擎执行代码时得出的真实作用域。