【问题标题】:javascript closures and object referencejavascript 闭包和对象引用
【发布时间】:2013-12-26 19:46:06
【问题描述】:

我的情况有点晦涩。主要是因为我认为我已经掌握了闭包。所以基本上我想要的是将集合重置为默认值。所以假设我有一个集合,它有带有对象数组参数的构造函数。

 var c = new collection([{x},{y},{z}]);

然后集合会定期更新。因为我没有将数组的初始值保存在某个地方,有时我想重置为初始值。

现在我不是在问如何实现这一点,我对闭包本身的问题可能有多种方式。请进一步阅读

所以我可能想用闭包来捕获这个初始值,所以它可能看起来像这样。

c.on('reset',(function(arr){

    return function(){

          c.internalarray = arr;

    }

 })(c.internalarray))

所以它似乎不起作用,因为引用作为参数传递,集合更新了据称被困的 arr 也得到了更新,它的最终结果总是正确的

arr === c.internalarray;

我可能会考虑传递数组的克隆,但重点不是在某个地方创建一个数据副本并分配保持全局变量。

所以我的问题是我做错了什么。我认为不知何故,javascript引擎会以某种方式隐式创建被困变量/对象的副本。我不必跟踪他们。

【问题讨论】:

    标签: javascript closures


    【解决方案1】:

    在 javascript 中,复合数据类型通过引用传递,因此,c.internalarrayarr 都引用相同的值。实际上,您的方法是正确的,但是您必须先复制该数组,然后再将其传递给您立即调用的函数。例如:

    c.on('reset', function (arr) {
        return function () {
            c.internalarray = arr;
        }
    }(c.internalarray.slice()));
    

    也就是说,这种技术不会创建深层副本,这意味着数组中包含的复合类型在立即调用的函数之外仍然是可变的。在下面的代码中,我试图简化这种情况,以使事情更容易理解,希望:

    变量a 指的是一个包含两个元素的数组:

    var a = [
        1, // primitive value
        {} // composite value
    ];
    

    让我们制作一个a 的浅拷贝并将其分配给b

    var b = a.slice();
    // a -> [1, {}]
    // b -> [1, {}]
    // b === a -> false
    

    a 中删除第一个元素对b 没有影响:

    a.shift();
    // a -> [{}]
    // b -> [1, {}]
    

    但是修改a中包含的对象也会影响b

    a[0].k = 'value';
    // a -> [{ k: "value" }]
    // b -> [1, { k: "value" }]
    // a[0] === b[1] -> true
    

    【讨论】:

    • 感谢您的澄清。好吧,我有点伤心。我期待更多。 os 据我从您的帖子中了解到,仅当您使用某些值类型时,闭包才有意义?众所周知的无处不在的循环示例非常适合具有递增值类型。如果它永远不会适用于引用类型,那么我可能会为其分配一些临时变量。
    • @mobygeek 闭包是关于变量的。对对象(或数组)的引用本身就是一个。这就是在闭包中保存的内容。但是,任何对象或数组的 内容 都可以更改,因为 JavaScript 对象是可变的。无论是否涉及关闭,这都是正确的。如果你想保存一个对象的内容,你必须明确地这样做。
    • @mobygeek “只有当你使用一些 v̶a̶l̶u̶e̶ 原始类型时,闭包才有意义”,而不是真的,“闭包可以让你将一些数据与对该数据进行操作的函数相关联”(MDN) ,这是它的主要目的,不管这个数据的原始/复合性质。在您的情况下,闭包对于对象的深层副本仍然有用。实际上,它能够存储数据以供以后使用,而不需要更复杂的面向对象样式,也不会污染上层范围。
    【解决方案2】:

    这与闭包无关。您的问题可以通过以下方式更简单地看到:

    var internalarray = [{x},{y},{z}];
    var arr = internalarray;
    
    // "update" internalarray here
    
    arr === internalarray // still true
    internalarray = arr; // This statement has no effect
    

    就像在 Java 中一样,JavaScript 中的每个值要么是原始值,要么是引用(指向对象的指针)。 arr 的值是指向对象的指针,c 的值是指向对象的指针,c.internalarray 的值是指向对象的指针等等。具体在这种情况下,c.internalarray 是指向数组对象的指针。将一个指针分配给另一个指针只会使第二个指针指向第一个指针指向的对象。当您执行arr = internalarray; 时(或在您的代码中,当您将c.internalarray 作为参数arr 传递给函数时),您有两个指向同一个对象的指针。

    当您说“收集定期更新”时,您永远不会分配(如在= 中)给c.internalarray 本身。这就是为什么arr === c.internalarray 仍然正确的原因——因为您最初将一个分配给另一个,所以它们可能不相等的唯一方法是您稍后分配给其中一个。相反,我猜您正在更改这两个变量指向的数组元素,使用像c.internalarray[foo] = bar; 这样的下标运算符或调用像c.internalarray.push(bar); 这样的方法。由于您正在更改指向的对象,指向它的多个指针将能够看到更改。

    【讨论】:

    • 好吧 newacct,谢谢你的贡献。并且我使用“按引用传递”这个词很糟糕,你说得对,我必须说清楚这意味着按值传递引用。
    • 我很清楚什么是参考,什么是价值。我没想到会在 javascript 中看到像 c# 中的“ref”关键字这样的东西。无论如何,重点是我最初认为即使我通过值传递引用(引用的副本不是对象),引擎也会创建它的某种深层副本并将其保存在关闭中。那是我的印象。仅此而已,似乎没有,有很多关于,炒作关闭。我们通常每天都用其他语言做。
    • 如果我错了,那么如果您打算销毁/忘记原始值,则关闭很有用。这样,由于有一个闭包(您的内部函数引用),它将为您保留。但如果您不打算丢弃原件,那么它们是相同的。没有必要依赖关闭。
    • @mobygeek:在这种情况下,使用闭包将arrc.internalarray 的原始值)分配给c.internalarray 是没有意义的,因为它们是相同的,因为你做了中间不分配给c.internalarray
    猜你喜欢
    • 1970-01-01
    • 2012-07-23
    • 1970-01-01
    • 1970-01-01
    • 2010-10-04
    • 2011-05-03
    • 1970-01-01
    • 1970-01-01
    • 2013-10-11
    相关资源
    最近更新 更多