Home  >  Article  >  Backend Development  >  到底什么是闭包?

到底什么是闭包?

WBOY
WBOYOriginal
2016-06-06 16:22:311170browse

回复内容:

JavaScript 闭包的本质源自两点,词法作用域和函数当作值传递。

词法作用域,就是,按照代码书写时的样子,内部函数可以访问函数外面的变量。引擎通过数据结构和算法表示一个函数,使得在代码解释执行时按照词法作用域的规则,可以访问外围的变量,这些变量就登记在相应的数据结构中。

函数当作值传递,即所谓的first class对象。就是可以把函数当作一个值来赋值,当作参数传给别的函数,也可以把函数当作一个值 return。一个函数被当作值返回时,也就相当于返回了一个通道,这个通道可以访问这个函数词法作用域中的变量,即函数所需要的数据结构保存了下来,数据结构中的值在外层函数执行时创建,外层函数执行完毕时理因销毁,但由于内部函数作为值返回出去,这些值得以保存下来。而且无法直接访问,必须通过返回的函数。这也就是私有性。

本来执行过程和词法作用域是封闭的,这种返回的函数就好比是一个虫洞,开了挂。也就是 @秋月凉 答案中轮子哥那句话的意思。

显然,闭包的形成很简单,在执行过程完毕后,返回函数,或者将函数得以保留下来,即形成闭包。实际上在 JavaScript 代码中闭包不要太常见。

函数作为第一等对象之后 JavaScript 灵活得不要不要的。 闭包其实是一个很通用的概念,正如 @寸志 老师说的:「闭包是词法作用域和函数作为First-class的共同体现」。 而很多大家耳熟能详的语言里都支持函数作为一类对象,比如JavaScript,Ruby,Python,C#,Scala,Java8.....,而这些语言里无一例外的都提供了闭包的特性,因为闭包可以大大的增强函数的处理能力,函数可以作为一类对象的这一优点才能更好的发挥出来。

那什么是闭包呢,一言以蔽之:一个持有外部环境变量的函数就是闭包。

理解闭包通常有着以下几个关键点:
1.函数
2.自由变量
3.环境

举个栗子:
<code class="language-js"><span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span>
<span class="kd">let</span> <span class="nx">b</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
<span class="p">}</span>
</code>
最近特别注意的看了一些关于闭包的东西,比方说这个
闭包的概念、形式与应用
算是大概理解了这么个东西,基本掌握了这货的用法,但对闭包意义的理解还是有点模糊,直到看到了这个 @vczh
到底什么是闭包? 感觉大部分回答太复杂了,过于理论化,绕来绕去,没抓住本质和精髓。其实闭包没那么复杂。

最简洁的回答:

闭包(Closure)就是一个有状态(不消失的私有数据)的函数(对象);或者,闭包就是一个有记忆的函数。

MDN 上的定义是这样的:

Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created.

Closures - JavaScript

首先,很明确,闭包是一个函数,一种比较特殊的函数。什么是函数?函数就是一个基本的程序运行逻辑单位(模块),通常有一组输入,有一个输出结果,内部还有一些进行运算的程序语句。所以,那些仅仅说闭包是作用域(scopes)或者其它什么的,是错误的,至少不准确。

MDN 中还有一段更详细的解释:

A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created.

下面是一个最简单的 JavaScript 闭包的例子,inc 就是一个闭包(函数/函式),它有一个私有数据(状态) count。

<code class="language-text">var inc = (function () { // 该函数体中的语句将被立即执行
  var count = 0; // 局部变量初始化
  return function () { // 返回一个内嵌的闭包函数引用
    return ++count; // 当外部函数 return 后,这里的 count 不再是外部函数的局部变量。
  };
}) ();
inc(); // count: 1
inc(); // count: 2
</code>
其他答案的解释都已经很详细了,我就不多写自己的理解了,但貌似都只有文字描述或许不方便新人理解,我就show我滴渣渣code吧,写四个例子啦,如果看明白了就大概懂了~
写四个理解JS闭包的例子 · Issue #8 · AutumnsWind/Good-text-Share · GitHub
第一个:
<code class="language-text">function love(name) {
                var text = 'Hello ' + name;
                var me = function() {
                    console.log(text);
                }
                return me;
            }
            var loveme = love('AutumnsWind');
            loveme(); // 输出 Hello AutumnsWind
</code>
首先,闭包是一个函数,而且是一个有权访问另一个函数作用域中的变量的函数。

最常见的闭包,就是在一个函数内部再创建一个函数。比如:

<code class="language-js"><span class="kd">function</span> <span class="nx">f</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nx">a</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code>
一点浅见

闭包就是当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之 外执行。

常见的例子

1.调用函数里面的函数(函数里面的函数能够保证外面的函数的作用域不会被销毁,所以无论是在函数里面还是在外面调用函数里面的函数都可以访问到外层函数的作用域,具体做法可以将里面函数当做返回值返回后通过两次的括号调用)

2.回调函数(回调函数会保留当前外层的作用域,然后回调到另一个地方执行,执行的时候就是闭包)

3.IIFE方式(严格算也不是闭包,就是(function(){})()这种格式)

闭包有什么用

个人浅见,由于闭包能保留所在的词法作用域,所以可以在外部使用这个作用域,通过闭包设置和获取里面作用域变量的值,,也能将外部的数据通过参数传进去作用域里面去执行,有点类似类的概念(可能说的有些不对)

循环和闭包

由于JavaScript没有块级作用域,所以循环直接是循环外面的作用域,如果循环是写在全局中,则循环里面的闭包保留的就是全局作用域,全局作用域只有一个,所以以下代码不能实现每隔一秒输出一个增加的数字(会连续输出5个6,变量i是全局变量,循环很快执行,i一下子变成6,所以后面输出的都是6)
<code class="language-js"><span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span><span class="o">=</span><span class="mi">1</span><span class="p">;</span> <span class="nx">i</span><span class="o"><span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
 <span class="nx">setTimeout</span><span class="p">(</span> <span class="kd">function</span> <span class="nx">timer</span><span class="p">()</span> <span class="p">{</span>
  <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span> <span class="nx">i</span> <span class="p">);</span>
 <span class="p">},</span> <span class="nx">i</span><span class="o">*</span><span class="mi">1000</span> <span class="p">);</span>
<span class="p">}</span>
</span></code>
相继式(Sequent)中的前件(Antesedent)。 不会js,就说闭包这个概念。
可以这么理解:
Closure这个closed的意思是这个环境是闭合的,里面运算不会依赖或者涉及到这个环境以外的东西。
所以一个函数的闭包就是它和它可能需要的所有外部的东西的最小集合,比如加上依赖的各种自由变量。 下面的图片出自gist.github.com/pauliri,这是在Chrome Developer Tools里一个图示javascript closure的例子,我觉得很有用,贴过来。
到底什么是闭包?
Closure是函数的可访问一个域,把这个域和window并列起来比较,这个概念就一点也不神秘了。
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn