JavaScript 闭包详解:从定义到原理与实例

作者:闭包旅人

时间:2026-10-30

浏览:286

本文深入解析JavaScript闭包的概念、形成机制及实际应用。通过对比《JavaScript高级程序设计》中的定义与词法作用域原理,详细剖析匿名函数如何持有对外部作用域的引用。结合具体代码示例,阐明变量查找规则及闭包在函数传递中的表现,帮助开发者彻底理解闭包的本质与常见误区。

文章目录

JavaScript 闭包详解:从定义到原理与实例

深入理解JavaScript——闭包

与许多前端初学者一样,我在接触闭包概念时投入了大量时间与精力进行探索。虽然最终取得了一定成效,但过程中的曲折与困惑是许多初学者共同面临的挑战。本文旨在梳理我的学习路径,分享在理解闭包过程中遇到的常见误区与解决方案,希望能为同样处于入门阶段的读者提供有价值的参考,欢迎指正交流。

闭包的定义与核心认知

关于闭包的定义,《JavaScript高级程序设计》将其描述为“有权访问另一个函数作用域中的变量的函数”。初读此定义时,往往感到模糊不清,难以在实际编码中灵活运用。即便听取他人分析时觉得有理有据,若缺乏对JavaScript作用域机制、匿名函数特性以及编译原理的基础认知,仍难以真正掌握。通过查阅多种资料并研读相关源码,我对闭包有了更直观的认识。其中一种观点认为:“闭包是基于词法作用域书写代码时所产生的自然结果,你甚至不需要为了利用它而有意为之的创建闭包,闭包的创建和使用是在你的代码中随处可见。你缺少的是根据你自己的意愿来识别,拥抱和影响闭包的思维环境。”这句话深刻揭示了闭包在JavaScript中的实际价值,值得细细体会。下文将通过具体实例展开讨论。

闭包实例与词法作用域查找规则

首先看一个简单的例子:


function createComparisonFunction(propertyName){
  return function(obj1,obj2){
    var value1=object1[propertyName];
    var value2=object2[propertyName];
    if(value1<value2){
      return -1;
    }else if(value1>value2){
      return 1;
    }else{
      return 0;
    }
  } 
}

上述代码返回一个匿名函数,该函数内部引用了外部变量value1和value2,同时调用了外层函数createComparisonFunction()的参数propertyName。回顾闭包的定义:有权访问另一个函数作用域中的变量的函数。此处返回的匿名函数确实能够访问外部函数作用域中的变量propertyName,因此符合闭包的特征。但从本质上看,这仅仅是基于词法作用域的变量查找规则,属于JavaScript中自然且易于理解的现象。

部分读者可能对“词法作用域的查找规则”感到陌生。简而言之,这是指根据变量的作用域链来查找并获取变量值的机制。以上例而言:createComparisonFunction函数的作用域包含变量propertyName和一个匿名函数(由于匿名函数没有名称,在createComparisonFunction内部无法直接调用,这也是匿名函数的一个局限,例如在事件监听中调用匿名函数时无法移除监听器,原理相同)。匿名函数的作用域则包含obj1、obj2、value1和value2四个变量。当匿名函数内部需要调用变量时,首先在其自身作用域中查询;若找到则直接使用;若未找到,则向外层作用域继续查找,直至全局作用域。如果全局作用域中仍未找到,则抛出ReferenceError(若找到已声明但未赋值的var a,由于a为undefined,后续操作可能报TypeError)。相反,createComparisonFunction函数只能在其自身作用域内查找变量,无法访问内部匿名函数的作用域。这种单向的查找规则即为词法作用域的查找规则(当然,这仅是基础规则之一)。

闭包的形成机制与内部函数传递

我们再来看一个例子:


function foo() {
  var a = 2;
  function bar() {
    console.log( a );
  }
  return bar;
}
var baz = foo();
baz();  //2

虽然答案可能显而易见,但其背后的实现原理值得深入分析。首先声明了一个函数foo,其作用域包含变量a和函数bar。foo函数内部定义了函数bar,bar内部没有声明新变量。接着定义变量baz,其值为foo()的返回值,即bar,因此baz=bar。最后一句baz()等价于bar()。根据词法作用域规则,bar是在foo函数内部声明的,理论上外部无法直接访问。这正是闭包的体现:再次回到定义,有权访问另一个函数作用域中的变量的函数。此处baz()(即bar)有权访问foo函数中的变量a(通过bar间接访问,原文表述为访问bar,但代码逻辑显示bar访问a,此处依原文逻辑修正为:baz()函数有权访问foo函数中的变量a,通过bar函数体现闭包)。这就是闭包。理解闭包后,常有人疑问:闭包是如何形成的?为何说它能访问foo中的变量?首先,闭包的常见形成方式是在一个函数内部创建另一个函数。当内部函数被传递到其词法作用域之外时,它仍然持有对原始定义作用域的引用,执行该函数便会使用闭包。

以上涵盖了闭包的基础要点,希望大家能仔细体会。后续将补充闭包的其他应用场景,以帮助大家拓展思维。

希望通过本文,大家能更好地掌握闭包知识。感谢大家对本站的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章