对闭包内对象的赋值就像一个指针

时间:2012-05-08 23:24:41

标签: javascript closures

我在javascript闭包中偶然发现了一个非常奇怪的行为。如果闭包的“全局”变量是一个对象,那么从它到另一个变量的任何赋值就像某种指针一样。一个简单的代码应该更好地解释我的意思:

function closure() {      
  var foo = {key1 : 'value 1'};
  return {
    method: function(){
      var bar = foo;
      bar.key2 = 'value2';
      return 'foo has attributes : ' + foo.key1 + ' and ' + foo.key2
    }
  }
}  
var someVar = closure();
someVar.method();  // returns "foo has attributes : value 1 and value2" (!!!)

我显然希望获得foo has attributes : value 1 and undefined,因为只有bar被修改了...但是,该脚本实际上修改了barfoo,让我感到困惑

我也注意到,当foobar是字符串或数字时,一切都按预期工作 - 修改bar不会影响foo

function closure() {
  var foo = 10;
  return {
    method: function(){
      var bar = foo; 
      bar += 1;
      return 'foo is : ' + foo + ' and bar is ' + bar
    }
  }
}
var someVar = closure();
someVar.method(); // returns "foo is : 10 and bar is 11"

我花了整整一个晚上试图弄清楚这种行为的原因......没有运气。我得到了相同的结果是SpiderMonkey和V8(都在Chrome和node.js上)。

我想要做的当然是从第一个例子修改bar而不影响foo。任何帮助将非常感激。

2 个答案:

答案 0 :(得分:2)

执行var bar = foo;时,barfoo都指向同一个对象。如果您不想更改foo,则需要克隆 foo。克隆时,您将获得一个全新的对象,bar将指向该对象。 This answer有一些关于在Javascript中克隆对象的信息。

此外,此行为不仅限于闭包。它将在常规Javascript代码中发生:

var foo = {a: 10, b: 6};
var bar = foo; 
bar.c = 7;
console.log(foo);

foo显示为包含ab c的属性。

您所看到的是大多数面向对象语言的标准行为。在处理对象时,您正在处理引用到这些对象而不是特定的实例值。您没有看到这个数字,因为在Javascript中它们是原始类型,因此您处理实际值而不是对这些值的引用。

答案 1 :(得分:2)

这是几乎所有面向对象语言的完全正常的预期行为,当然是我能想到的所有动态的语言。变量保存对象的引用,而不是对象本身。它就像一个指针,但是从内存位置的细节中抽象出来;你不能对它做任何算术。就使用它而言,它看起来像变量直接保存对象,但事实并非如此。如果将对象传递给函数,即使JavaScript是按值传递,该函数也可以修改对象。如果将它分配给另一个变量,则不会获得副本,而是别名:两个指向同一对象的变量。

如果要制作副本,则必须明确这样做。没有内置方法可以自动执行此操作,但请参阅this question了解某些选项。