JavaScript作用域与闭包详解:从ES5到ES6的演变

作者:拾光时间:2026-10-24 17:14:03浏览:84

本文深入解析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作用域与闭包的详情介绍完毕,更多相关内容请搜索本站历史文章或继续浏览后续文章,感谢支持!

作者最新文章