作用域
作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问,作用域也可以堆叠成层次结构,子作用域可以访问父作用域,反过来则不行。
JavaScript 的作用域分为:
- 全局作用域:脚本模式运行所有代码的默认作用域
- 模块作用域:模块模式中运行代码的作用域
- 函数作用域:由函数创建的作用域
- 块级作用域:用一对花括号(一个代码块)创建出来的作用域
全局作用域
定义
<script>
标签 和 .js
文件 的【最外层】就是全局作用域, 全局作用域中的变量可以在整个代码中访问。在此声明的变量在函数内部也可以被访问。
示例
var globalVar = "I am global";
function showGlobal() {
console.log(globalVar); // 输出: I am global
}
showGlobal();
注意:
- 全局变量容易引发命名冲突和难以调试的问题。尽量避免在全局作用域中声明变量。防止全局变量被污染
- 全局变量在整个应用程序的生命周期内存在,可能导致内存泄漏。使用后应尽量清理不再需要的全局变量。
模块作用域
定义
ES6 引入了模块(Modules)概念,每个模块都有自己的作用域。模块内部定义的变量和函数默认是私有的,外部无法直接访问,只有通过 export
导出后才能被其他模块使用。
示例
javascript// module.js
let moduleVar = "I am a module variable"; // 模块作用域
export const exportedVar = "I am exported";
// main.js
import { exportedVar } from './module.js';
console.log(exportedVar); // 输出: I am exported
console.log(moduleVar); // ReferenceError: moduleVar is not defined
注意事项
- 命名导出和默认导出:模块可以有命名导出和默认导出,理解两者的区别非常重要。命名导出可以导出多个,而默认导出只能有一个。
- 模块依赖:模块之间的依赖关系需要注意,避免循环依赖的问题。
函数作用域
定义
函数作用域是指在函数内部定义的变量和函数只能在该函数内访问。每个函数都有自己的作用域,函数内部可以访问外部的变量,但外部不能访问内部的变量。
示例
javascriptfunction myFunction() {
const localVar = "I am local";
console.log(localVar); // 输出: I am local
}
myFunction();
console.log(localVar); // ReferenceError: localVar is not defined
注意事项
- 变量提升:使用
var
声明的变量会被提升到函数顶部,可能导致意想不到的行为。 - 闭包:在函数内部定义的函数可以访问外部函数的变量,这种特性称为闭包。闭包可以用于创建私有变量,但也可能导致内存泄漏。
总结:
- 函数内部声明的变量,在函数外部无法被访问
- 函数的参数也是函数内部的局部变量
- 不同函数内部声明的变量无法互相访问
- 函数执行完毕后,函数内部的变量实际被清空了
块级作用域
定义
块级作用域是指在 {}
大括号内定义的作用域,通常出现在条件语句、循环和其他代码块中。使用 let
和 const
声明的变量具有块级作用域,而 var
声明的变量不具备块级作用域。
示例
javascript{
let blockVar = "I am block scoped";
var blockVar2 = "I am function scoped";
console.log(blockVar); // 输出: I am block scoped
}
console.log(blockVar); // ReferenceError: blockVar is not defined
console.log(blockVar2); // 输出: I am function scoped
注意事项
let
和const
的使用:使用let
和const
可以避免变量提升的问题,尽量优先使用这两者。- 变量访问:确保在定义变量的块内访问,否则会导致错误。
总结:
- let 声明的变量和const 声明的常量会产生块作用域,var 不会产生块作用域
- 不同代码块之间的变量无法互相访问
- 推荐使用 let 或 const
1.3 作用域链
作用域链本质上是底层的 变量查找机制 。
当 JavaScript 引擎查找变量时,它会按照作用域链的规则进行查找。作用域链是由当前作用域和外部作用域组成的,查找从当前作用域开始,向外层作用域逐层查找,直到全局作用域。
//全局作用域
let a = 1
let c = 1
//局部作用域
function fn1() {
let b = 2
let c = 2
//局部作用域
function fn2() {
c = 3
console.log(a,b,c)
}
fn2()
}
fn1()
// 1 2 3
总结:
- 嵌套关系的作用域串联起来形成了作用域链
- 相同作用域链中按着 从小到大 的规则查找变量
- 子作用域能够访问父作用域,父级作用域无法访问子级作用域