[1 tool called]
JavaScript 作用域和作用域链详解
作用域和作用域链是 JavaScript 中的核心概念,它们决定了变量和函数的可访问性和查找机制。
作用域 (Scope)
作用域是指变量和函数的可访问范围,它决定了代码的哪个部分可以访问特定的变量或函数。
1. 全局作用域 (Global Scope)
在代码最外层定义的变量和函数属于全局作用域:
// 全局作用域
var globalVar = 'I am global'
let globalLet = 'Global let'
const globalConst = 'Global const'
function globalFunction() {
console.log('I am a global function')
}
// 在任何地方都可以访问
console.log(globalVar) // 'I am global'
console.log(window.globalVar) // 'I am global' (浏览器环境)2. 函数作用域 (Function Scope)
在函数内部定义的变量只能在该函数内部访问:
function functionScope() {
var functionVar = 'I am in function scope'
let functionLet = 'Function let'
console.log(functionVar) // 可以访问
console.log(functionLet) // 可以访问
function innerFunction() {
console.log(functionVar) // 可以访问外层函数的变量
}
innerFunction()
}
functionScope()
// console.log(functionVar) // ReferenceError: functionVar is not defined3. 块级作用域 (Block Scope)
ES6 引入的 let 和 const 具有块级作用域:
// var 没有块级作用域
if (true) {
var varVariable = 'var variable'
let letVariable = 'let variable'
const constVariable = 'const variable'
}
console.log(varVariable) // 'var variable' - 可以访问
// console.log(letVariable) // ReferenceError: letVariable is not defined
// console.log(constVariable) // ReferenceError: constVariable is not defined
// 循环中的块级作用域
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i) // 输出: 0, 1, 2
}, 100)
}
for (var j = 0; j < 3; j++) {
setTimeout(() => {
console.log(j) // 输出: 3, 3, 3
}, 100)
}作用域链 (Scope Chain)
作用域链是 JavaScript 引擎查找变量的机制,它是一个由内向外的查找链条。
作用域链的形成
var globalVar = 'global'
function outerFunction(outerParam) {
var outerVar = 'outer'
function middleFunction(middleParam) {
var middleVar = 'middle'
function innerFunction(innerParam) {
var innerVar = 'inner'
// 作用域链查找顺序:
// 1. innerFunction 作用域: innerVar, innerParam
// 2. middleFunction 作用域: middleVar, middleParam
// 3. outerFunction 作用域: outerVar, outerParam
// 4. 全局作用域: globalVar
console.log(innerVar) // 'inner' - 在当前作用域找到
console.log(middleVar) // 'middle' - 在上一级作用域找到
console.log(outerVar) // 'outer' - 在更上一级作用域找到
console.log(globalVar) // 'global' - 在全局作用域找到
}
return innerFunction
}
return middleFunction
}
const middle = outerFunction('outer param')
const inner = middle('middle param')
inner('inner param')作用域链的可视化表示
作用域链查找过程:
innerFunction 作用域
↓ (未找到则向上查找)
middleFunction 作用域
↓ (未找到则向上查找)
outerFunction 作用域
↓ (未找到则向上查找)
全局作用域
↓ (未找到)
ReferenceError
词法作用域 (Lexical Scoping)
JavaScript 使用词法作用域(也称为静态作用域),这意味着作用域在代码编写时就已经确定:
var name = 'global'
function foo() {
console.log(name) // 输出什么?
}
function bar() {
var name = 'local'
foo() // 调用 foo
}
bar() // 输出: 'global'
// 解释:foo 函数在定义时就确定了其作用域链
// 它的作用域链是:foo作用域 -> 全局作用域
// 而不是:foo作用域 -> bar作用域 -> 全局作用域闭包与作用域链
闭包是作用域链的重要应用:
function createCounter() {
let count = 0
return function() {
count++
console.log(count)
}
}
const counter1 = createCounter()
const counter2 = createCounter()
counter1() // 1
counter1() // 2
counter2() // 1 - 独立的作用域链
// 每个闭包都保持着对其创建时的作用域链的引用闭包中的作用域链
function outerFunction(x) {
return function middleFunction(y) {
return function innerFunction(z) {
return x + y + z // 可以访问所有外层参数
}
}
}
const partial = outerFunction(1) // x = 1 被保存在闭包中
const morepartial = partial(2) // y = 2 被保存在闭包中
const result = morepartial(3) // z = 3, 计算 1 + 2 + 3
console.log(result) // 6执行上下文与作用域链
作用域链是执行上下文的重要组成部分:
// 执行上下文包含:
// 1. 变量对象 (Variable Object)
// 2. 作用域链 (Scope Chain)
// 3. this 绑定
function example() {
var a = 1
function inner() {
var b = 2
console.log(a + b) // 通过作用域链访问 a
}
inner()
}
example()
/*
执行过程:
1. 创建全局执行上下文
- 作用域链: [全局变量对象]
2. 调用 example(),创建执行上下文
- 作用域链: [example变量对象, 全局变量对象]
3. 调用 inner(),创建执行上下文
- 作用域链: [inner变量对象, example变量对象, 全局变量对象]
*/总结
作用域的核心概念:
- 决定变量和函数的可访问范围
- 分为全局作用域、函数作用域和块级作用域
- JavaScript 使用词法作用域(静态作用域)
作用域链的核心概念:
- 变量查找的机制:从内向外逐层查找
- 由执行上下文的变量对象组成的链式结构
- 是闭包和许多高级特性的基础
最佳实践:
- 尽量使用
let和const而不是var - 避免创建意外的全局变量
- 理解闭包对内存的影响
- 在性能敏感的场景中考虑作用域链的查找成本