[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 defined

3. 块级作用域 (Block Scope)

ES6 引入的 letconst 具有块级作用域:

// 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 使用词法作用域(静态作用域)

作用域链的核心概念:

  • 变量查找的机制:从内向外逐层查找
  • 由执行上下文的变量对象组成的链式结构
  • 是闭包和许多高级特性的基础

最佳实践:

  • 尽量使用 letconst 而不是 var
  • 避免创建意外的全局变量
  • 理解闭包对内存的影响
  • 在性能敏感的场景中考虑作用域链的查找成本