JavaScript 闭包与匿名函数

2024-05-05

我和我的一个朋友目前正在讨论 JS 中什么是闭包,什么不是。我们只是想确保我们真正正确地理解它。

我们以这个例子为例。我们有一个计数循环,想要在控制台上延迟打印计数器变量。因此我们使用setTimeout and closures捕获计数器变量的值,以确保它不会打印 N 倍的值 N。

错误的解决方案没有closures或任何接近closures将会:

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

这当然会打印 10 倍的值i循环之后,即10。

所以他的尝试是:

for(var i = 0; i < 10; i++) {
    (function(){
        var i2 = i;
        setTimeout(function(){
            console.log(i2);
        }, 1000)
    })();
}

按预期打印 0 到 9。

我告诉他他没有使用closure捕捉i,但他坚持认为他是。我证明他不使用closures将 for 循环体放入另一个循环体中setTimeout(将他的匿名函数传递给setTimeout),再次打印 10 次 10。如果我将他的函数存储在一个中,这同样适用var并执行它after循环,也打印 10 次 10。所以我的论点是他真的不capture的价值i,制作他的版本not一个关闭。

我的尝试是:

for(var i = 0; i < 10; i++) {
    setTimeout((function(i2){
        return function() {
            console.log(i2);
        }
    })(i), 1000);
}

所以我捕捉i (named i2在闭包内),但现在我return另一个函数并传递它。就我而言,传递给 setTimeout 的函数确实捕获了i.

现在谁在使用闭包,谁没有?

请注意,这两个解决方案都会延迟在控制台上打印 0 到 9,因此它们解决了原始问题,但我们想了解这两个解决方案中的哪一个使用闭包来实现这一目标。


编者注:JavaScript 中的所有函数都是闭包,如此处所述post http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/。然而,我们只对识别这些函数的子集感兴趣,它们是有趣的 https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures从理论的角度来看。从此以后任何对这个词的引用closure除非另有说明,否则将引用该函数子集。

对闭包的简单解释:

  1. 取一个函数。我们姑且称之为F吧。
  2. 列出F的所有变量。
  3. The variables may be of two types:
    1. 局部变量(绑定变量)
    2. 非局部变量(自由变量)
  4. 如果 F 没有自由变量,那么它不能是闭包。
  5. If F has any free variables (which are defined in a parent scope of F) then:
    1. F 必须只有一个父作用域a自由变量是绑定的。
    2. If F is 引用的从外面that父作用域,那么它就变成了闭包that自由变量。
    3. That自由变量称为闭包 F 的上值。

现在让我们用它来找出谁使用闭包,谁不使用(为了解释起见,我命名了这些函数):

案例 1:您朋友的程序

for (var i = 0; i < 10; i++) {
    (function f() {
        var i2 = i;
        setTimeout(function g() {
            console.log(i2);
        }, 1000);
    })();
}

上面的程序中有两个函数:f and g。让我们看看它们是否是闭包:

For f:

  1. List the variables:
    1. i2 is a local多变的。
    2. i is a free多变的。
    3. setTimeout is a free多变的。
    4. g is a local多变的。
    5. console is a free多变的。
  2. Find the parent scope to which each free variable is bound:
    1. i is bound到全球范围。
    2. setTimeout is bound到全球范围。
    3. console is bound到全球范围。
  3. In which scope is the function referenced? The global scope.
    1. Hence i is not 关闭 by f.
    2. Hence setTimeout is not 关闭 by f.
    3. Hence console is not 关闭 by f.

因此函数f不是一个闭包。

For g:

  1. List the variables:
    1. console is a free多变的。
    2. i2 is a free多变的。
  2. Find the parent scope to which each free variable is bound:
    1. console is bound到全球范围。
    2. i2 is bound到范围f.
  3. In which scope is the function referenced? The scope of setTimeout.
    1. Hence console is not 关闭 by g.
    2. Hence i2 is 关闭 by g.

因此函数g是自由变量的闭包i2(这是一个上值g) when it's 引用的从内部setTimeout.

对你不好:你的朋友正在使用闭包。内部函数是一个闭包。

案例 2:您的程序

for (var i = 0; i < 10; i++) {
    setTimeout((function f(i2) {
        return function g() {
            console.log(i2);
        };
    })(i), 1000);
}

上面的程序中有两个函数:f and g。让我们看看它们是否是闭包:

For f:

  1. List the variables:
    1. i2 is a local多变的。
    2. g is a local多变的。
    3. console is a free多变的。
  2. Find the parent scope to which each free variable is bound:
    1. console is bound到全球范围。
  3. In which scope is the function referenced? The global scope.
    1. Hence console is not 关闭 by f.

因此函数f不是一个闭包。

For g:

  1. List the variables:
    1. console is a free多变的。
    2. i2 is a free多变的。
  2. Find the parent scope to which each free variable is bound:
    1. console is bound到全球范围。
    2. i2 is bound到范围f.
  3. In which scope is the function referenced? The scope of setTimeout.
    1. Hence console is not 关闭 by g.
    2. Hence i2 is 关闭 by g.

因此函数g是自由变量的闭包i2(这是一个上值g) when it's 引用的从内部setTimeout.

对你有好处:您正在使用闭包。内部函数是一个闭包。

所以你和你的朋友都在使用闭包。别再争论了。我希望我已经清楚了闭包的概念以及如何为你们俩识别它们。

Edit:关于为什么所有函数都是闭包的简单解释(鸣谢@Peter):

首先让我们考虑以下程序(它是control http://jsfiddle.net/KyQKw/):

lexicalScope();

function lexicalScope() {
    var message = "This is the control. You should be able to see this message being alerted.";

    regularFunction();

    function regularFunction() {
        alert(eval("message"));
    }
}
  1. 我们知道,两者lexicalScope and regularFunction不是关闭从上面的定义.
  2. 当我们执行程序时我们期待 message被提醒because regularFunction不是一个闭包(即它可以访问all其父作用域中的变量 - 包括message).
  3. 当我们执行程序时我们观察到 that message确实是警觉了。

接下来让我们考虑以下程序(它是选择 http://jsfiddle.net/KyQKw/1/):

var closureFunction = lexicalScope();

closureFunction();

function lexicalScope() {
    var message = "This is the alternative. If you see this message being alerted then in means that every function in JavaScript is a closure.";

    return function closureFunction() {
        alert(eval("message"));
    };
}
  1. 我们知道,只有closureFunction是一个闭包从上面的定义.
  2. 当我们执行程序时我们期待 message不被警告because closureFunction是一个闭包(即它只能访问它的所有非局部变量 at 函数创建的时间 (看到这个答案 https://stackoverflow.com/a/12930799/783743) - 这不包括message).
  3. 当我们执行程序时我们观察到 that message实际上正在被警告。

我们由此推断出什么?

  1. JavaScript 解释器对待闭包的方式与对待其他函数的方式没有什么不同。
  2. 每个函数都有它的作用域链 http://dmitrysoshnikov.com/ecmascript/chapter-4-scope-chain/与它一起。闭包没有separate参考环境。
  3. 闭包就像其他所有函数一样。当它们发生时我们就称它们为闭包引用的在一定范围内outside他们所属的范围because这是一个有趣的案例。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaScript 闭包与匿名函数 的相关文章

  • React中ComponentDidMount生命周期方法被调用两次

    在我的 React 应用程序中 加载应用程序时会进行两次初始 API 调用 我查看了 Chrome gt inpsect 中的网络选项卡 启动器 调用堆栈显示第一个调用是从VM123000 bundle js而第二个调用只是从实际的bund
  • 如何将 blob 文件附加到 HTML href="mailto:" 中

    我有一个可通过 URL 获取的文件 需要授权 我创建了一个 mailto 链接 并希望将此文件附加到邮件中 我怎样才能做到这一点 类似于 mailto 电子邮件受保护 cdn cgi l email protection attachmen
  • Javascript拆分正则表达式问题

    你好 我正在尝试我认为在 Javascript 中相当简单的正则表达式 但给我带来了很多麻烦 我希望能够通过 javascript 通过 和 分割日期 var date 02 25 2010 var myregexp2 new RegExp
  • 使用 Google Apps 脚本处理数组中输入元素中的多个文件

    我有一个表单 允许从下拉列表中选择一个项目并上传文件 项目的名称和 ID 保存在电子表格文档中 适用于一个文件 但我想上传多个文件 你能帮我修改一下脚本吗 HTML 部分如下所示 div class col md 4 col sm 6 di
  • 谷歌脚本循环性能

    我是 google 脚本的新手 我不确定为什么与 Excel VBA 的简单循环相比 我的性能如此差 我附上了下面的代码 它是一个大约 1200 行的循环 每秒删除大约 2 3 行 我写的脚本效率很低吗 我还不熟悉 Javascript 但
  • iOS Javascript DOM“冻结?”

    这里有几个问题 有没有办法阻止 iOS 在滚动时冻结页面上的 javascript 当您在另一个选项卡中或切换应用程序时 iOS 是否会冻结 JavaScript iOS 上还有其他主要的 javascript 限制吗 iOS 6 x 会暂
  • 在 Nodejs/javascript 中的 Excel 中创建动态数量的列或标题

    我用过exceljsNodejs中用于将json数据导出到excel的模块 它工作正常 但必须在添加行之前预定义标题 列的名称 即列是固定的 添加行后 我无法动态添加列 我尝试了许多通过 npm 提供的模块 但它们都具有相同的功能 那么 有
  • 从一个页面导航到另一个页面时,JavaScript 不会执行

    我正在构建我的第一个 Ruby on Rails 应用程序 并尝试创建一个动画导航栏 我正在使用 jQuery 和 Turbolink 这是我的application js under app assets javascripts docu
  • jQuery show() 和 hide() 的更流畅替代方案

    我有一个带有隐藏列的页面设置 使用 jQuery show 和 hide 函数将列滑入和滑出 然而 它有点 笨重 并且在显示 隐藏时看起来不太流畅 相比之下 我还有一个使用 jquery UI 手风琴的页面部分 当在这些部分之间切换时 过渡
  • 从 iframe 关闭父弹出窗口

    在domain1 com 上 我有一个链接 可以打开domain2 com 的弹出窗口 弹出窗口中有一个 iframe 也托管在 domain2 com 上 在 iframe 中 我试图关闭弹出窗口 我似乎对如何做到这一点感到困惑 在 if
  • 代理递归函数

    想象一个简单的递归函数 我们试图包装它以检测输入和输出 A simple recursive function const count n gt n 1 count n 1 Wrap a function in a proxy to ins
  • 用于匹配重复子字符串的单个js正则表达式?

    假设我有一个字符串 例如 where is mummy where is daddy 我想用空字符串替换任何一组重复子字符串 所以在这种情况下where and is元素将被删除 结果字符串将是 mummy daddy 我想知道是否有任何单
  • 我可以阻止history.popstate在初始页面加载时触发吗?

    我正在开发一个通过 AJAX 提供内容的网站 如果您单击菜单中的某个项目 内容 div 会更新为 get回应 没什么花哨的 我正在实施history pushState允许使用浏览器的后退 前进按钮进行导航 我有以下内容可以在历史导航上加载
  • 如何使用Javascript获取ASP.NEt Web Forms标签的值?

    我有以下标签控件
  • 类型错误:require.config 不是一个函数

    我正在使用 require js 作为早午餐项目的一部分 这段代码抛出错误 require config require config is not a function paths jquery lib jquery underscore
  • 如何使用 Soundcloud api 将流传输到 html5 音频播放器中?

    我刚刚开始学习 javascript 作为我的第一次尝试 我想创建自定义音频播放器 它使用 soundcloud 的 api 作为音乐源 到目前为止 这就是我的设置
  • Angular2 - 防止复选框被选中

    我有一个每行包含一个复选框的表 在表头中 我有一个Check All切换所有表格行框的复选框 我正在尝试实现一些逻辑 如果复选框的数量将超过特定限制 则显示错误并且不切换表行复选框或checkall盒子本身 有一个问题允许checkAll即
  • 处理照片上传的最佳方式是什么?

    我正在为一个家庭成员的婚礼制作一个网站 他们要求的一个功能是一个照片部分 所有客人都可以在婚礼结束后前往并上传他们的照片 我说这是一个很棒的想法 然后我就去实现它 那么只有一个问题 物流 上传速度很慢 现代相机拍摄的照片很大 2 5 兆 我
  • CSS 或 Javascript - 如果背景图像未加载,则显示后备文本[重复]

    这个问题在这里已经有答案了 如果徽标图形文件未加载或丢失 如何显示文本而不是徽标 我有带有背景 PNG 图像的 div div class iHaveBgImage this text should be displayed if bg i
  • 如何运行 Mike Bostock 的 D3 示例?

    我一直在尝试经营迈克博斯托克透视地球仪 http bl ocks org mbostock 6747043例如 但是如果您尝试在本地重现它 则对其 json 文件的引用是不正确的 问题来自于这行代码 d3 json mbostock raw

随机推荐