
函数作用域和块级作用域是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共同点
都是作用域机制 :两者的核心目的都是控制变量的可见性和生命周期,防止变量污染。
都支持嵌套 :函数作用域可以嵌套函数,块级作用域也可以嵌套块级作用域。
都遵循作用域链 :访问变量时,会从当前作用域开始,逐级向上查找,直到全局作用域。
实际开发中的应用
函数作用域 :适用于需要在整个函数范围内访问的变量,或需要利用变量提升特性的场景。
块级作用域 :适用于只在特定代码块内使用的变量,如循环变量、条件分支中的临时变量,有助于提高代码的可读性和可维护性。
在现代JavaScript开发中,推荐优先使用块级作用域( let 和 const ),因为它提供了更严格的变量控制,减少了意外的变量泄漏和覆盖问题。