为什么在函数调用中捕获对象的值?

2023-12-02

当您单击此代码时,应该会弹出一个带有图像编号的警报:

for(var i=0; i<10; i++) {
    $("#img" + i).click(
        function () { alert(i); }
    );
}

你可以看到它不起作用http://jsfiddle.net/upFaJ/。我知道这是因为所有点击处理程序闭包都引用同一个对象i,因此每个处理程序在触发时都会弹出“10”。

但是,当我这样做时,效果很好:

for(var i=0; i<10; i++) {
    (function (i2) {
        $("#img" + i2).click(
            function () { alert(i2); }
        );
    })(i);
}

你可以看到它工作在http://jsfiddle.net/v4sSD/.

为什么它有效?依然只有一个i内存中的对象,对吧?对象始终通过引用传递,而不是复制,因此自执行函数调用应该没有区别。两个代码片段的输出应该相同。那么为什么是i对象被复制 10 次?为什么它有效?

我觉得这个版本很有趣不起作用:

for(var i=0; i<10; i++) {
    (function () {
        $("#img" + i).click(
            function () { alert(i); }
        );
    })();
}

看来将对象作为函数参数传递会产生很大的差异。


编辑:好的,所以前面的例子可以用原语来解释(i) 按值传递给函数调用。但是这个使用真实对象的示例又如何呢?

for(var i=0; i<5; i++) {
    var toggler = $("<img/>", { "src": "http://www.famfamfam.com/lab/icons/silk/icons/cross.png" });
    toggler.click(function () { toggler.attr("src", "http://www.famfamfam.com/lab/icons/silk/icons/tick.png"); });
    $("#container").append(toggler);
}

不工作:http://jsfiddle.net/Zpwku/

for(var i=0; i<5; i++) {
    var toggler = $("<img/>", { "src": "http://www.famfamfam.com/lab/icons/silk/icons/cross.png" });
    (function (t) {
        t.click(function () { t.attr("src", "http://www.famfamfam.com/lab/icons/silk/icons/tick.png"); });
        $("#container").append(t);
    })(toggler);
}

在职的:http://jsfiddle.net/YLSn6/


大多数答案都是正确的,因为将对象作为函数参数传递会破坏闭包,从而允许我们从循环内将事物分配给函数。但我想指出why情况就是如此,而且这不仅仅是闭包的特例。

你看,javascript 将参数传递给函数的方式与其他语言有点不同。首先,它似乎有两种实现方式,具体取决于它是原始值还是对象。对于原始值来说seems按值传递并为对象传递seems通过引用传递。

javascript 如何传递函数参数

实际上,JavaScript 的真正解释解释了这两种情况,以及它为何破坏闭包,仅使用一种机制。

javascript所做的实际上就是传递参数通过参考副本。也就是说,它创建了对该参数的另一个引用,并将该新引用传递给函数。

按值传递?

假设javascript中的所有变量都是引用。在其他语言中,当我们说变量是引用时,我们期望它的行为如下:

var i = 1;
function increment (n) { n = n+1 };
increment(i); // we would expect i to be 2 if i is a reference

但在 javascript 中,情况并非如此:

console.log(i); // i is still 1

这是一个经典的值传递,不是吗?

通过引用传递?

但是等等,对于对象来说,情况就不同了:

var o = {a:1,b:2}
function foo (x) {
    x.c = 3;
}
foo(o);

如果参数按值传递,我们期望o对象保持不变,但是:

console.log(o); // outputs {a:1,b:2,c:3}

这是经典的引用传递。因此,根据传递原始类型或对象的天气情况,我们有两种行为。

等等,什么?

但等一下,看看这个:

var o = {a:1,b:2,c:3}
function bar (x) {
    x = {a:2,b:4,c:6}
}
bar(o);

现在看看会发生什么:

console.log(o); // outputs {a:1,b:2,c:3}

什么!这不是通过引用传递!数值不变!

这就是为什么我称它为传递参考副本。如果我们这样想的话,一切就都说得通了。我们不需要认为基元在传递给函数时具有特殊行为,因为对象的行为方式相同。如果我们尝试修改变量指向的对象,那么它的工作方式就像按引用传递,但如果我们尝试修改引用本身,那么它的工作方式就像按值传递。

这也解释了为什么通过将变量作为函数参数传递来破坏闭包。因为函数调用会创建另一个像原始变量一样不受闭包约束的引用。

尾声:我撒谎了

在我们结束之前还有一件事。我之前说过,这统一了原始类型和对象的行为。其实不是,原始类型还是有区别的:

var i = 1;
function bat (n) { n.hello = 'world' };
bat(i);
console.log(i.hello); // undefined, i is unchanged

我放弃。这没有任何意义。事情就是这样。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么在函数调用中捕获对象的值? 的相关文章

随机推荐

  • Java i++操作说明[重复]

    这个问题在这里已经有答案了 可能的重复 a b 和 a b 以及 a 和 a 有什么区别 x x 之后的 x 是什么 在 Test1 中 i 将其值增加 1 并返回旧值 并将其增量值保留在 i 变量中 但在 Test2 中 i 将其值增加
  • 构建期间的模块依赖缓存问题

    最近改用 Go 1 11 版本 并尝试将我们的项目转换为使用新的模块系统 但是 我遇到了缓存系统的令人沮丧的问题 到目前为止我已经运行过GOCACHE off因为过去不相关的问题 但这不是模块的选项 下面的命令日志基于使用 Homebrew
  • 使用 JavaScript 获取字符串长度(以像素为单位)

    假设我有字符串 Hello 这个字符串的长度显然是五个字符 但是它的像素长度是多少 JavaScript 有没有一种简单的方法来确定这个长度 我想到了一个解决方案 必须向用户显示一个额外的 div 但这种方式看起来很老套且复杂 从更大的角度
  • 如何将 Outlook 2007 收件箱中定义的子文件夹中收到的电子邮件中附加的 Excel 文件保存到 Windows 上的文件夹中?

    我需要将 Outlook 邮件中收到的 Excel 附件保存在收件箱中的特定子文件夹 每日最终 中 并知道该子文件夹中的所有电子邮件都将包含该 Excel 附加文件 我下面有一个使用 excel VBA 的示例 但它不起作用 所以请建议我
  • 我们如何使用 API 访问特定的 Google Analytics 帐户数据?

    我们正在开发一个网络应用程序 允许客户创建和显示产品 我们希望通过我们的 Google Analytics 分析 帐户向客户提供有关其管理面板中的产品的指标 不幸的是 GA API 文档没有说明如何执行此操作 所有示例都基于 OAuth 2
  • 通过单击文本字段来选择日期[重复]

    这个问题在这里已经有答案了 我试图通过单击文本字段来实现日期选择器 我正在使用中提到的代码这个链接 但我没有得到输出 如果有人知道如何通过单击文本字段来显示日期选择器 请为我提供一些解决方案 提前致谢 我看到您的链接并得到了解决方案 因此请
  • jQTouch在AJAX页面加载时执行代码

    当内容是静态时 a href nearme Click a 当内容是AJAX时 a href page html Click a 如何绑定 AJAX page html 加载后发生的事情 这个问题与 jqtouch 有关 它从常规锚标记发出
  • 使用 Web 部署跳过 XML 文档文件

    我正在尝试使用 Web Deploy 发布 NET Web 服务 目前 它在包中包含 XML 文档文件 我在 Visual Studio 的项目属性的 生成 选项卡中取消选中 XML 文档文件 这会阻止发布该 XML 文件 但该项目引用了许
  • 如何在 RabbitMQ 中设置重试次数?

    我正在使用 RabbitMQ 并且有一个保存电子邮件消息的队列 我的消费者服务使消息出队并尝试发送它们 如果由于任何原因我的消费者无法发送消息 我想重新排队消息以再次发送 我意识到我可以执行 basicNack 并将重新排队标志设置为 tr
  • Android SDK 管理器的 GUI 消失了吗?

    我很少为 Android 做一些事情 所以我有点困惑 以前有两种类型的安装 Android Studio 和 Android SDK 我有IDEA 所以不需要Studio 通常我会下载带有 UI 工具来下载其组件的 SDK 我刚得到http
  • cakephp 模型验证错误消息未显示在 hasOne 关联中

    我想要做model验证与association以单一形式 我有两张桌子users 父表 和user details 子表 现在模型验证仅适用于用户表 我也希望它适用于 userDetails 表 它们之间的关系是hasOne 验证仅适用于用
  • CSS 2.1 规范:不折叠父级边距的基本原理(当父级是浮动的或具有除可见之外的溢出时)

    The CSS 2 1 规范 第 8 3 1 节在折叠边距上指出 建立新块格式化上下文的元素的边距 例如 因为浮动和带有 可见 以外的 溢出 的元素 不会 与他们流入的孩子一起崩溃 我花了一段时间才意识到块格式化上下文是 由父母建立并应用于
  • Spring security 自定义 FilterInitationSecurityMetadataSource 实现 403 禁止问题

    简而言之 我正在尝试实现一个自定义 FilterInitationSecurityMetadataSource 以便使用 spring security 5 0 6 和 Spring Boot 2 0 3 在我的 Web 应用程序中动态保护
  • 在 Symfony 2 中使用 gzip / 压缩而不使用 mod_deflate

    我正在研究两个不同的Symfony 2 8项目运行在不同的服务器上 它想使用压缩来加快加载速度 我找到的所有资源都指向mod deflate 但是虽然第一台服务器不提供mod deflate根本 第二个服务器无法使用mod deflate
  • 使用 Powershell Out-Printer 到文件时控制输出位置

    我有一个 Powershell 脚本 它从服务器上的文件夹中检索所有图像文件 jpg png 并将它们 打印 到一个文件 特别是使用特定打印驱动程序的 prn 文件 所有这些都运行良好 问题是我无法弄清楚如何控制 打印 的输出的位置 即它将
  • 如何在 vb.net 中闪烁/闪烁任务栏图标?

    我需要使我的 vb net 应用程序能够在应用程序中收到通知时闪烁 闪烁以吸引用户的注意 就像此图中的 DW 图标一样 我已经在谷歌上搜索了一段时间 并尝试了各种代码示例 但都没有成功 这是我到目前为止所得到的 Public Class F
  • [[NSDate date] keep] 和 [[NSDate alloc] init] 之间的区别

    由于以下两者具有相同的目的 today NSDate date retain and today NSDate alloc init 那么它们之间有什么区别呢 这里的任何事情都与内存分配方法有关 或者其他什么是相应地使用它们的原因 NSDa
  • 如何左右对齐 Flexbox 列?

    使用典型的 CSS 我可以将两列中的一列向左浮动 另一列向右浮动 中间有一些装订线空间 我该如何使用 Flexbox 来做到这一点 http jsfiddle net 1sp9jd32 container width 500px borde
  • Perl:V 5.8.8:在 CentOS5/RHEL5 上找不到 auto/XML/LibXSLT/new.al

    我正进入 状态 无法找到 auto XML LibXSLT new al 我的 CentOS5 机器上安装 Perl 5 8 8 时出错 此问题与 libxml2 和 perl 模块有关XML LibXML XML LibXSLT 对于 1
  • 为什么在函数调用中捕获对象的值?

    当您单击此代码时 应该会弹出一个带有图像编号的警报 for var i 0 i lt 10 i img i click function alert i 你可以看到它不起作用http jsfiddle net upFaJ 我知道这是因为所有