0

我在 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被修改过的......但是,脚本实际上修改了两者bar并且foo让我很困惑。

我还注意到,当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 上)。

我要做的当然是在不影响 foo 的情况下修改第一个示例中的 bar。任何帮助将不胜感激。

4

2 回答 2

2

当你这样做时var bar = foo;,两者barfoo都指向同一个对象。如果您不想foo更改,则需要克隆 foo. 当你克隆时,你会得到一个全新的对象,并且bar会指向它。这个答案有一些关于在 Javascript 中克隆对象的信息。

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

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

将显示foo具有属性a,b和。 c

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

于 2012-05-08T23:28:08.900 回答
2

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

如果你想复制,你必须明确地这样做。没有内置的方法可以自动执行此操作,但请参阅此问题以获取一些选项。

于 2012-05-08T23:30:59.170 回答