首页 web前端 js教程 JavaScript中的闭包(Closure)详细介绍_javascript技巧

JavaScript中的闭包(Closure)详细介绍_javascript技巧

May 16, 2016 pm 04:23 PM
javascript 闭包

闭包是JavaScript中一个重要的特性,其最大的作用在于保存函数运行过程中的信息。在JavaScript中,闭包的诸多特性源自函数调用过程中的作用域链上。

 

函数调用对象与变量的作用域链

 

对于JavaScript中的每一次函数调用,JavaScript都会创建一个局部对象以储存在该函数中定义的局部变量;如果在该函数内部还有一个嵌套定义的函数(nested function),那么JavaScript会在已经定义的局部对象之上再定义一个嵌套局部对象。对于一个函数,其内部有多少层的嵌套函数定义,也就有多少层的嵌套局部对象。该局部对象称为“函数调用对象”(ECMAScript 3中的“call object”,ECMAScript 5中改名为“declarative environment record”,但个人认为还是ECMAScript 3中的名称更容易理解一些)。以下面的函数调用为例:


复制代码 代码如下:

function f(x){
  var a = 10;
  return a*x;
}
console.log(f(6));//60

在这个简单的例子中,当调用f()函数时,JavaScript会创建一个f()函数的调用对象(姑且称之为f_invokeObj),在f_invokeObj对象内部有两个属性:a和x;运行f()时,a值为10而x值为6,因此最后的返回结果为60。图示如下:

当存在函数嵌套时,JavaScript将创建多个函数调用对象:


复制代码 代码如下:

function f(x){
  var a = 10;
  return a*g(x);
  function g(b){
    return b*b;
  }
}
console.log(f(6));//360


在这个例子中,当调用f()函数时,JavaScript会创建一个f()函数的调用对象(f_invokeObj),其内部有两个属性a和x,a值为10而x值为6;运行f()时,JavaScript会对f()函数中的g()函数进行解析定义,并创建g()的调用对象(g_invokeObj),其内部有一个属性b,b值与传入参数x相同为6,因此最后的返回结果为360。图示如下:

可以看到,函数调用对象形成了一条链。当内嵌函数g()运行,需要获取变量值的时候,会从最近的函数调用对象中开始进行搜索,如果无法搜索到,则沿函数调用对象链在更远的调用对象中进行搜寻,此即所谓的“变量的作用域链”。如果两个函数调用对象中出现相同的变量,则函数会取离自己最近的那个调用对象中的变量值:


复制代码 代码如下:

function f(x){
  var a = 10;
  return a*g(x);
  function g(b){
    var a = 1;
    return b*b*a;
  }
}
console.log(f(6));//360, not 3600


在上面的例子中,g()函数的调用对象(g_invokeObj)和f()函数的调用对象(f_invokeObj)中均存在变量a且a的值不同,当运行g()函数时,在g()函数内部所使用的a值为1,而在g()函数外部所使用的a值则为10。图示此时的函数调用对象链如下:

什么是闭包?

在JavaScript中所有的函数(function)都是对象,而定义函数时都会产生相应的函数调用对象链,一次函数定义对应一个函数调用对象链。只要函数对象存在,相应的函数调用对象就存在;一旦某函数不再被使用,相应的函数调用对象就会被垃圾回收掉;而这种函数对象和函数调用对象链之间的一一组合,就称之为“闭包”。在上面f()函数和g()函数的例子中,就存在两个闭包:f()函数对象和f_invokeObj对象组成了一个闭包,而g()函数对象和g_invokeObj-f_invokeObj对象链一起组成了第二个闭包。当g()函数执行完毕后,由于g()函数不再被使用,因此g()闭包被垃圾回收了;之后,当f()函数执行完毕后,由于同样的原因,f()闭包也被垃圾回收了。

从闭包的定义可以得出结论:所有的JavaScript函数在定义后都是闭包 – 因为所有的函数都是对象,所有的函数在执行后也都有其对应的调用对象链。

不过,令闭包真正发挥作用的是嵌套函数的情况。由于内嵌函数是在外部函数运行的时候才开始定义的,因此内嵌函数的闭包中所保存的变量值(尤其是外部函数的局部变量值)是这次运行过程中的值。只要内嵌函数对象依然存在,那么其闭包就依然存在(闭包中的变量值不会发生任何改变),从而也就实现了保存函数运行过程的信息这个目的。考虑以下这个例子:


复制代码 代码如下:

var a = "outside";
function f(){
  var a = "inside";
  function g(){return a;}
  return g;
}
var result = f();
console.log(result());//inside


在这个例子中,当运行f()函数时,g()函数被定义,同时创建了g()函数的闭包,g()闭包包含了g_invokeObj-f_invokeObj对象链,因此保存了f()函数执行过程中的变量a的值。当执行console.log()语句时,由于g函数对象仍然存在,因此g()闭包也依然存在;当运行这个仍然存在的g函数对象时,JavaScript会使用依然存在的g()闭包并从中获取变量a的值(“inside”)。

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1664
14
CakePHP 教程
1423
52
Laravel 教程
1317
25
PHP教程
1268
29
C# 教程
1246
24
C++ lambda 表达式中闭包的含义是什么? C++ lambda 表达式中闭包的含义是什么? Apr 17, 2024 pm 06:15 PM

在C++中,闭包是能够访问外部变量的lambda表达式。要创建闭包,请捕获lambda表达式中的外部变量。闭包提供可复用性、信息隐藏和延迟求值等优势。它们在事件处理程序等实际情况中很有用,其中即使外部变量被销毁,闭包仍然可以访问它们。

C++ 函数中闭包的优点和缺点是什么? C++ 函数中闭包的优点和缺点是什么? Apr 25, 2024 pm 01:33 PM

闭包是一种嵌套函数,它能访问外层函数作用域的变量,优点包括数据封装、状态保持和灵活性。缺点包括内存消耗、性能影响和调试复杂性。此外,闭包还可以创建匿名函数,并将其作为回调或参数传递给其他函数。

解决闭包导致的内存泄漏问题 解决闭包导致的内存泄漏问题 Feb 18, 2024 pm 03:20 PM

标题:闭包引起的内存泄漏及解决方法引言:闭包是JavaScript中一个非常常见的概念,它可以让内部函数访问外部函数的变量。然而,闭包在使用不当的情况下可能导致内存泄漏。本文将探讨闭包引起的内存泄漏问题,并提供解决方法及具体代码示例。一、闭包引起的内存泄漏问题闭包的特性是内部函数可以访问外部函数的变量,这意味着在闭包中引用的变量不会被垃圾回收。如果使用不当,

C++ Lambda 表达式如何实现闭包? C++ Lambda 表达式如何实现闭包? Jun 01, 2024 pm 05:50 PM

C++Lambda表达式支持闭包,即保存函数作用域变量并供函数访问。语法为[capture-list](parameters)->return-type{function-body}。capture-list定义要捕获的变量,可以使用[=]按值捕获所有局部变量,[&]按引用捕获所有局部变量,或[variable1,variable2,...]捕获特定变量。Lambda表达式只能访问捕获的变量,但无法修改原始值。

PHP 函数的链式调用和闭包 PHP 函数的链式调用和闭包 Apr 13, 2024 am 11:18 AM

是的,可以通过链式调用和闭包优化代码简洁性和可读性:链式调用可将函数调用链接为一个流畅接口。闭包可创建可重用代码块,并在函数外部访问变量。

函数指针和闭包对Golang性能的影响 函数指针和闭包对Golang性能的影响 Apr 15, 2024 am 10:36 AM

函数指针和闭包对Go性能的影响如下:函数指针:稍慢于直接调用,但可提高可读性和可复用性。闭包:通常更慢,但可封装数据和行为。实战案例:函数指针可优化排序算法,闭包可创建事件处理程序,但会带来性能损失。

golang函数闭包在测试中的作用 golang函数闭包在测试中的作用 Apr 24, 2024 am 08:54 AM

Go语言函数闭包在单元测试中发挥着至关重要的作用:捕获值:闭包可以访问外部作用域的变量,允许在嵌套函数中捕获和重用测试参数。简化测试代码:通过捕获值,闭包消除了对每个循环重复设置参数的需求,从而简化了测试代码。提高可读性:使用闭包可以组织测试逻辑,使测试代码更清晰、更易于阅读。

golang匿名函数和闭包的优缺点总结 golang匿名函数和闭包的优缺点总结 May 05, 2024 am 09:54 AM

匿名函数简洁、匿名,但可读性差、调试困难;闭包能封装数据、管理状态,但可能导致内存消耗和循环引用。实战案例:匿名函数可用于简单数值处理,闭包可实现状态管理。

See all articles