函数作用域和块级作用域是JavaScript中控制变量可见性和生命周期的两种重要机制。它们既有联系又有明显区别,下面从多个角度进行详细对比。

基本概念

函数作用域

函数作用域 是指变量在声明它的函数内部及其嵌套函数中可访问,外部无法访问。这是ES5及之前版本JavaScript中主要的作用域类型。

function myFunction() {
  var functionVar = '函数作用域变量';
  console.log(functionVar); // 正常访问:'函数作用域变量'
}
myFunction();
console.log(functionVar); // 错误:functionVar is not defined

块级作用域

块级作用域 是ES6(ECMAScript 2015)引入的概念,指变量在一对花括号{}内声明时,仅在该花括号范围内可访问。

if (true) {
  let blockVar = '块级作用域变量';
  console.log(blockVar); // 正常访问:'块级作用域变量'
}
console.log(blockVar); // 错误:blockVar is not defined

主要区别

详细对比解析

1. 作用域范围的差异

函数作用域覆盖整个函数,包括函数内的所有代码块;而块级作用域仅覆盖花括号内的代码。

function example() {
  if (true) {
    var functionScoped = '我在函数内任何地方都可见';
    let blockScoped = '我只在这个if块内可见';
  }
  console.log(functionScoped); // 正常输出:'我在函数内任何地方都可见'
  console.log(blockScoped); // 错误:blockScoped is not defined
}
2. 变量提升与暂时性死区

在函数作用域中,使用 var 声明的变量会发生 变量提升 ,即变量声明会被提升到函数的顶部,但赋值不会提升:

function hoistingExample() {
  console.log(hoistedVar); // undefined(变量声明被提升)
  var hoistedVar = '变量提升了';
}

而在块级作用域中,使用 let 和 const 声明的变量不存在变量提升,且存在 暂时性死区 ,在声明前访问会抛出错误:

function tdzExample() {
  console.log(nonHoistedVar); // 错误:Cannot access 'nonHoistedVar' before initialization
  let nonHoistedVar = '没有变量提升';
}
3. 循环中的行为差异

这是函数作用域和块级作用域最明显的差异之一:

// 使用var(函数作用域)
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0); // 输出:3, 3, 3
}
console.log(i); // 输出:3(变量泄漏到外部)

// 使用let(块级作用域)
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0); // 输出:0, 1, 2
}
console.log(j); // 错误:j is not defined(变量不会泄漏)
4. 全局对象属性

在全局作用域中使用 var 声明的变量会成为全局对象(如浏览器中的 window )的属性,而使用 let 和 const 声明的变量不会:

var globalVar = '全局变量';
let globalLet = '全局let变量';

console.log(window.globalVar); // 输出:'全局变量'
console.log(window.globalLet); // 输出:undefined

共同点

  1. 都是作用域机制 :两者的核心目的都是控制变量的可见性和生命周期,防止变量污染。

  2. 都支持嵌套 :函数作用域可以嵌套函数,块级作用域也可以嵌套块级作用域。

  3. 都遵循作用域链 :访问变量时,会从当前作用域开始,逐级向上查找,直到全局作用域。

实际开发中的应用

  • 函数作用域 :适用于需要在整个函数范围内访问的变量,或需要利用变量提升特性的场景。

  • 块级作用域 :适用于只在特定代码块内使用的变量,如循环变量、条件分支中的临时变量,有助于提高代码的可读性和可维护性。

在现代JavaScript开发中,推荐优先使用块级作用域( let 和 const ),因为它提供了更严格的变量控制,减少了意外的变量泄漏和覆盖问题。