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。让我们为它附加一个点击事件监听器。
现在您需要计算按钮被点击的次数。有两种方法可以这样做。
<button id="button">Click me</button>
<script>
let count = 0;
const button = document.getElementById("button");
button.addEventListener("click", ()=>{
count++;
console.log(count);
})
</script>
<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))
函数可以在以下场景中使用闭包:
除非真正需要,否则建议避免关闭,因为它们会降低应用程序的性能。使用闭包会消耗大量内存,如果闭包处理不当,可能会导致内存泄漏。
关闭的变量不会被 JavaScript 的垃圾收集器释放。当您在闭包内使用变量时,垃圾收集器不会释放内存,因为浏览器认为变量仍在使用中。因此,这些变量会消耗内存并降低应用程序的性能。
这是一个示例,展示了闭包内的变量如何不会被垃圾收集。
function f(){
const x = 3;
return function inner(){
console.log(x);
}
}
f()();
这里的变量 x即使不经常使用也消耗内存。垃圾收集器将无法释放此内存,因为它位于闭包内。
掌握 JavaScript 是一项无止境的任务,因为有很多概念和框架通常不会由经验丰富的开发人员自己探索。通过学习基础知识并经常练习,您可以显着提高对 JavaScript 的掌握。迭代器和生成器是 JavaScript 面试中面试会问到的一些概念。
标签: JavaScript 编程 编码技巧