什么是 JavaScript 闭包以及如何编写它们?

JavaScript 是最难掌握的编程语言之一。有时,即使是高级开发人员也无法预测他们编写的代码的输出。JavaScript 中更令人困惑的概念之一是闭包。初学者通常会被这个概念绊倒——别担心。这篇文章会慢慢带你通过基本的例子来帮助你更好地理解闭包。让我们开始吧。

闭包是函数及其词法环境的结构,包括在创建闭包时函数作用域中的任何变量。简单来说,考虑一个外部函数和一个内部函数。内部函数将可以访问外部函数的作用域。

在查看一些 JavaScript 闭包示例之前,您需要了解词法范围。

词法环境是本地内存及其父环境。查看下面给出的示例并猜测以下代码的输出:

function outer(){ 
let a = 10;
console.log(y);
inner();
function inner(){
console.log(a);
console.log(y);
}
}
let y = 9;
outer();

输出将为9 , 10 , 9。内部函数可以访问其父函数(outer()函数)的变量。因此,inner()函数可以访问变量 a。的内()函数也可以访问变量y,因为概念的范围链。

外部函数的父函数是全局的,inner()函数的父函数是outer()函数。因此,inner()函数可以访问其父项的变量。如果您尝试访问全局范围内的变量a,则会显示错误。因此,你可以说,内()函数是词法内外部()函数,和的词汇父外()函数是全球性的。

既然你已经了解了词法环境,你可以很容易地猜出以下代码的输出:

function a(){
let x = 10;
function b(){
console.log(x);
}
b();
}
a();

输出为10。虽然乍一看您可能不会猜到,但这是 JavaScript 中的闭包示例。闭包只不过是一个函数及其词法环境。

让我们考虑一个示例,其中三个函数相互嵌套:

function a(){ 
let x = 10;
function b(){
function c(){
function d(){
console.log(x);
}
d();
}
c();
}
b();
}
a();

它还会被称为闭包吗?答案是:是的。同样,闭包是一个带有其词法父级的函数。函数d()的词法父项是c(),由于作用域链的概念,函数d()可以访问外部函数和全局函数的所有变量。

再看一个有趣的例子:

function x(){
let a = 9;
return function y(){
console.log(a);
}
}
let b = x();

在 JavaScript 中,您可以在函数内部返回函数,将函数分配给变量,以及在函数内部传递函数。这就是语言的美妙之处。如果打印变量b ,你能猜出输出是什么吗?它将打印函数y()。函数x()返回一个函数y()。因此,变量b存储一个函数。现在,你能猜出如果调用变量b会发生什么吗?它打印变量a的值:9

您还可以使用闭包实现数据隐藏。为了更好地理解,请考虑一个按钮的示例,该按钮在浏览器上具有名为“button”的 id。让我们为它附加一个点击事件监听器。

现在您需要计算按钮被点击的次数。有两种方法可以这样做。

  • 创建一个全局变量 count 并在单击时增加它。但是这种方法有一个缺陷。在全局变量中进行修改很容易,因为它们很容易访问。
    <button id="button">Click me</button>
    <script>
    let count = 0;
    const button = document.getElementById("button");

    button.addEventListener("click", ()=>{
    count++;
    console.log(count);
    })
    </script>
  • 我们可以通过使用闭包来实现数据隐藏。您可以将整个addEventListener()函数包装在一个函数中。它关闭。创建闭包后,您可以创建一个计数变量并在单击时增加其值。使用此方法,变量将保留在功能范围内,无法进行任何修改。
    <button id="button">Click me</button>
    <script>
    const button = document.getElementById("button");
    function countOnClick(){
    let count = 0;
    button.addEventListener("click", ()=>{
    count++;
    console.log(count);
    })
    }
    countOnClick();
    </script>

    闭包不仅在 JavaScript 中非常重要,在其他编程语言中也是如此。它们在许多场景中很有用,您可以在它们的私有范围内创建变量或组合函数,以及其他情况。

    考虑这个函数组合的例子:

    const multiply = (a,b) => a*b;
    const multiplyBy2 = x => multiply(10, x);
    console.log(multiplyBy2(9));

    我们可以使用闭包实现相同的示例:

    const multiply = function(a){
    return function (b){
    return a*b
    }
    }
    const multiplyBy2 = multiply(2);
    console.log(multiplyBy2(10))

    函数可以在以下场景中使用闭包:

  • 实现函数柯里化
  • 用于数据隐藏
  • 与事件监听器一起使用
  • 用于 setTimeout method()
  • 除非真正需要,否则建议避免关闭,因为它们会降低应用程序的性能。使用闭包会消耗大量内存,如果闭包处理不当,可能会导致内存泄漏。

    关闭的变量不会被 JavaScript 的垃圾收集器释放。当您在闭包内使用变量时,垃圾收集器不会释放内存,因为浏览器认为变量仍在使用中。因此,这些变量会消耗内存并降低应用程序的性能。

    这是一个示例,展示了闭包内的变量如何不会被垃圾收集。

     function f(){
    const x = 3;
    return function inner(){
    console.log(x);
    }
    }
    f()();

    这里的变量 x即使不经常使用也消耗内存。垃圾收集器将无法释放此内存,因为它位于闭包内。

    掌握 JavaScript 是一项无止境的任务,因为有很多概念和框架通常不会由经验丰富的开发人员自己探索。通过学习基础知识并经常练习,您可以显着提高对 JavaScript 的掌握。迭代器和生成器是 JavaScript 面试中面试会问到的一些概念。

    标签: JavaScript 编程 编码技巧