JavaScript作用域与闭包详解:从ES5到ES6的演变
本文深入解析JavaScript中的作用域与闭包机制,涵盖ES5的全局与函数作用域,以及ES6引入的块级作用域。详细阐述作用域链、词法作用域的工作原理,并通过代码示例说明闭包的创建条件、实际应用及可能导致的内存泄漏问题,最后解析高频面试题。
1、作用域
简言之,作用域指代码中变量定义的区域,它决定了当前执行代码对变量的访问权限。
在ES5中,通常存在两种作用域类型:
- 全局作用域:作为程序的最外层作用域,其生命周期贯穿整个程序。
- 函数作用域:仅在函数定义时创建,且嵌套于父级函数作用域或全局作用域内。
阐述概念后,分析以下代码示例:
var a = 100
function test(){
var b = a * 2
var a = 200
var c = a/2
console.log(b)
console.log(c)
}
test() // 这里会打印出什么?
解析:
- 该代码构建了全局作用域与函数作用域。
- 全局作用域包含变量a,其初始赋值为100。
- 在test函数作用域内,定义了局部变量b、a、c。
- 此处涉及变量提升机制,函数作用域内首先执行var b; var a; var c;的提升操作。
- 随后对b进行赋值,此时a尚未被赋值,故a值为undefined,执行a*2后b结果为NaN。
- 接着将a重新赋值为200,c赋值为a/2即100。
因此最终控制台输出结果为 NaN,100。
ES6新增了一种块级作用域机制。
简言之,花括号{...}内的区域即为块级作用域,但Javascript并非原生支持块级作用域,需借助ES6规范中提出的let、const来构建块级作用域。
// ES5
if(true) {
var name = '南玖'
}
console.log(name) // 南玖
// ES6
if(true) {
let age = 18
}
console.log(age) // 这里会报错
2、作用域链
当可执行代码访问变量时,优先查找当前作用域,若存在则返回;否则逐级向父级作用域查找,直至全局作用域。这种嵌套查找机制被称为作用域链。
3、词法作用域
词法作用域是作用域的一种工作模型,词法作用域是JavaScript语言中使用的作用域类型,亦被称为静态作用域。
词法作用域指变量和块的作用域由代码编写时的位置决定,属于静态作用域。函数作用域取决于其声明位置,与实际调用位置无关。
MDN对闭包的定义如下:
函数与其周围(词法环境)引用的捆绑组合即为闭包(closure)。
这意味着闭包允许内层函数访问外层函数的作用域。在JavaScript中,每次创建函数时,闭包随之生成。
基于上述分析可得出:
闭包 = 函数 + 外层作用域
先观察以下代码片段:
var name = '前端南玖'
function say() {
console.log(name)
}
say()
解析:say函数能够访问外层作用域的变量a,这是否构成了闭包?
《Javascript权威指南》指出:严格意义上,所有JavaScript函数均为闭包。
这仅是理论层面的闭包,与日常使用存在差异。前述示例仅为简单闭包。
ECMAScript对闭包的定义:
- 理论上:所有函数都是闭包,因为创建时已保存上层上下文数据。
- 实践中:闭包需满足两个条件:1. 引用了外层作用域的变量;2. 即使创建它的上下文已销毁,它依然存在。
再参考《JavaScript权威指南》中的代码示例:
let scope = 'global scope'
function checkscope(){
let scope = 'local scope'
function f(){
return scope
}
return f
}
let s = checkscope()
s() // 这里返回什么?
许多同学可能认为结果是global scope,但事实果真如此吗?让我们分析其执行过程:
- 首先执行全局代码,创建全局执行上下文,定义并赋值全局变量
scope。 - 声明
checkscope函数,创建其执行上下文,定义并赋值局部变量scope。 - 声明f函数,创建该函数的执行上下文。
- 执行
checkscope函数,该函数返回f函数并赋值给变量s。 - 执行s函数,等同于执行f函数。此处返回的
scope值为local scope。至于为何是local scope,前文已阐述词法
作用的基本规则:JavaScript函数使用定义它们的作用域执行。在定义f函数的作用域中,变量scope的值为local scope。
5、闭包的应用
闭包主要应用于维护内部变量的场景。
6、闭包的缺陷
- 闭包可能导致变量常驻内存,使用不当会引发内存泄漏。
- 内存泄漏可能导致应用程序卡顿或崩溃。
7、高频闭包面试题
var arr = []
for(var i=0;i<3;i++){
arr[i] = function(){
console.log(i)
}
}
arr[0]() // 3
arr[1]() // 3
arr[2]() // 3
// 这里在执行的时候i已经变成了3
// 使用闭包解决
var arr = []
for(var i=0;i<3;i++){
arr[i] = (function(i){
return function(){
console.log(i)
}
})(i)
}
arr[0]() // 0
arr[1]() // 1
arr[2]() // 2
至此,关于Javascript作用域与闭包的详情介绍完毕,更多相关内容请搜索本站历史文章或继续浏览后续文章,感谢支持!
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
