在JavaScript中描述闭包概念

在JavaScript中描述闭包概念

在本文中,您将了解JavaScript中的闭包概念以及如何将其应用于各种函数。JavaScript中的闭包是一种将所有函数组合并绑定到词法环境中的方式。随着我们在本文中的深入,我们将深入探讨闭包的概念。

先决条件: 在我们继续进行主要讨论之前,您应该具备良好的JavaScript函数工作知识,即内存分配的方式,变量作用域概念以及JavaScript中的词法环境。

让我们首先讨论基本的JavaScript程序示例:

如果您不知道如何在HTML文档中添加和执行JavaScript,请参考《在HTML文档中放置JavaScript的位置?》一文以获得进一步的详细知识。

我们将快速查看JavaScript代码的执行方式。为此,在index.html文件中添加<script>标签引用外部JavaScript代码或者在<script>标签中定义代码。然后,在浏览器上打开开发者控制台选项卡以查看代码的输出。

index.html

<!DOCTYPE html> 
<html> 
  
<head> 
    <title>Closures</title> 
</head> 
  
<body> 
    <h2>Closures in Javascript</h2> 
    <script src="./script.js"></script> 
</body> 
  
</html> 
JavaScript

现在,我们将了解所有基本概念,例如函数的工作原理,如何在作用域链中声明一个函数,如何从函数中返回函数,通过示例之后,我们将理解闭包的概念。

示例1: 在这里,我们只是简单地说明函数的工作原理如下:

script.js

function a() { 
  var x = 5; 
  console.log(x); 
} 
  
function b() { 
  var y = 15; 
  console.log(y); 
} 
a(); 
b();
JavaScript

解释:

  1. 首先,创建一个全局执行上下文,然后函数定义会在内存中占用空间。
  2. 当javascript遇到第一个函数调用a()时,它创建另一个执行上下文,并为变量x分配内存,x中放入undefined。
  3. 然后进入执行线程阶段,变量x得到值5,在下一行console.log打印x的值,最后当函数执行完毕时,它从调用栈中移除,并且存储变量x的执行上下文被销毁,函数b()的情况也一样。
  4. 但是在此我们无法在代码块外部访问变量x和y,因为当函数完成其执行上下文时,执行上下文也会被删除,当x消失时,在内存中无法找到它。函数b()也是一样的,我们无法在该函数之外访问变量y。

输出:

在JavaScript中描述闭包概念

示例2: 在这个部分,我们将讨论使用作用域链访问变量的函数中的函数。

script.js

function a() { 
  var x = 5, 
    y = 6; 
  console.log(x); 
  
  function b() { 
    var z = 7; 
    console.log(y); 
    console.log(z); 
  } 
  b(); 
} 
a();
JavaScript

解释:

  1. 在这里创建了一个执行上下文,然后函数a()获得了内存中的空间。
  2. 然后我们调用函数a(),所以它得到了自己的执行上下文。
  3. 这里又有一个变量 xfunction b() ,它们在内存中占用了空间,然后在代码执行阶段,变量x被打印出来。
  4. 一旦控制权转移到调用函数b(),它在最后一个执行上下文中获得了另一个执行上下文,并且变量y在此后获得了内存中的空间,然后遇到了console.log(x),但是x不在函数b()的执行上下文中,所以根据作用域链,它去了父词法环境中找到了并打印了x的值。
  5. 变量y是函数内部的东西,因此无需任何额外的工作即可打印出来。

输出:

在JavaScript中描述闭包概念

示例3: 在这种情况下,我们将讨论从函数返回函数。

script.js

function a() { 
  console.log("Hello, I am a function"); 
  
  function b() { 
    console.log("Hello, I am an inner function"); 
  } 
  return b; 
} 
const result = a(); 
result();
JavaScript

解释:

  1. 全局执行上下文创建,变量result和函数a()分配内存空间。
  2. 稍后在调用函数a()时,另一个执行上下文将被创建,并且函数b()将在该内存空间中分配。
  3. 最后,在打印一行并返回一个函数后,a()完成其执行并从调用栈中删除。同时,它的执行上下文也被删除。
  4. 函数a()的结果存储在const变量中。
  5. 我们成功调用了函数result(),因此返回的函数b()内的功能被执行。

    输出:

在JavaScript中描述闭包概念

示例4: 现在是闭包部分。假设不仅有一个简单的console.log,而且在函数b()内部还声明了一些变量。现在,请停下来并思考一下代码的输出结果。

script.js

function outer() { 
  var x = 5; 
  
  function inner() { 
    console.log("Hello, I am an inner function"); 
    console.log( 
      "I am accessing var x of my parent function"
      + " when that parent functions execution "
      + "context destroyed"
    ); 
    console.log(x); 
  } 
  return inner; 
} 
const result = outer(); 
result();
JavaScript

解释:

  1. 全局执行上下文被创建,变量result和函数a()在内存中获得空间。
  2. 在线程执行阶段,首先调用a()函数。
  3. 创建一个新的执行上下文,var x和函数b在内存中获取空间,然后函数b返回,这意味着我们将函数的代码作为结果,并且现在函数a()从调用栈中移除,它的执行上下文也被销毁。
  4. 在最后一行,当我们调用函数result()时,它成功开始执行其中的代码。但是,一旦它遇到一个不在该函数执行上下文中的变量,它会尝试在外部作用域中找到var x,但是等等,我们无法访问函数a()的执行上下文,那么x将如何被执行?
  5. 这是指var x将变成未定义吗?不是的,因为 这就是闭包的概念。 当我们从另一个函数暴露或返回函数时,不仅返回的代码,而且还带有一个特殊的东西,被称为词法环境,它表示其外部父函数的环境。
  6. 因此,函数b()当从函数返回时,它带有其父函数的词法环境,因此始终可以访问变量x的引用。请注意最后一行 始终可以访问变量的引用 ,而不是值。

    输出:

在JavaScript中描述闭包概念

定义: 闭包是将函数与其词法环境捆绑在一起的组合。词法环境是本地函数内存和对父环境的引用。换句话说,当子函数保留父作用域的环境时,闭包在父函数执行完之后创建。

在JavaScript中描述闭包概念

这个对父级词法环境的引用是闭包函数能够访问外部函数的变量的原因,即使这些函数不在调用堆栈中,或者我们可以说即使外部函数已经关闭了。

闭包是如何创建的: 在创建另一个函数的同时,当一个函数正在被创建时,闭包就会被创建。在上面的示例中,函数inner在创建函数a()的同时被创建。

一些闭包的用例和示例:

1. 隐私性: 每当我们需要隐藏一些功能或变量作为封装时,我们就将它们包裹在父级的词法环境中。

示例: 下面的示例是使用闭包进行封装的基本模板。

解释:

  1. 所有全局执行上下文将在内存分配阶段创建,函数outer()和const闭包函数将在内存中占用空间。
  2. 在线程执行时,函数outer内的代码开始执行,因为它是代码中的第一行。
  3. 现在会创建另一个执行上下文,const变量notAccessibleByAnyone将占用空间,然后外部函数返回内部函数代码,执行上下文将消失。请注意,const变量不在执行上下文中。
  4. 但是闭包函数引用了该变量,这就是闭包封装的整个概念。

script.js

function outer(secret) { 
  const notAccessibleByAnyone = secret; 
  
  return function inner() { 
    console.log(notAccessibleByAnyone); 
  }; 
} 
  
const closureFunction = outer("Secret Data"); 
closureFunction(); 
console.log(notAccessibleByAnyone);
JavaScript

输出: 在第一行中, 当闭包访问时,打印”秘密数据”,而当外部任何人试图访问时,JavaScript会抛出ReferenceError错误。

在JavaScript中描述闭包概念

2. 偏函数: 当我们需要创建某种功能,其中存在一种常见的模式时,我们创建一个接受较少参数的父函数,并创建一个比父函数接受的参数更少的内部函数。这个概念也被称为高阶函数,其中我们从函数中返回函数。

示例: 下面的示例提供了偏函数的基本示例。

说明:

  1. 我们创建了一个父函数,父函数接收一个乘数作为参数,然后将其传递给内部函数。
  2. 稍后当我们调用父函数时,它返回内部函数的代码,该内部函数始终可以访问在调用父函数时提供的乘数。
  3. 函数multiplyBy2()包含一个函数,该函数接受一个数组,并对其进行循环运算,然后返回修改后的数组,其中的元素被乘以2。
  4. 函数multiplyBy4()包含一个函数,该函数接受一个数组,并对其进行循环运算,然后返回修改后的数组,其中的元素被乘以4。

script.js

function partialApplication(multiplier) { 
  return function inner(array) { 
    let size = array.length; 
    for (let i = 0; i < size; i++) { 
      array[i] = array[i] * multiplier; 
    } 
    return array; 
  }; 
} 
  
const multiplyBy2 = partialApplication(2); 
const arr1 = multiplyBy2([1, 2, 3]); 
console.log(arr1); 
  
const multiplyBy4 = partialApplication(4); 
const arr2 = multiplyBy4([1, 2, 3]); 
console.log(arr2);
JavaScript

输出:

在JavaScript中描述闭包概念

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程

登录

注册