2013-05-27 61 views
1
var a = [3, 4, 5]; 
var b = [6, 7, 8]; 

function why() { 
    b = a; 
    b[0] = 1; 
    alert(a[0] + ' ' + b[0]); 
} 
why(); 

结果是a[0]=1, b[0]=1;。它似乎喜欢JavaScript通过引用传递?为什么JavaScript通过引用传递?

但在这种情况下:

var a = [3, 4, 5]; 
var b = [6, 7, 8]; 

function why() { 
    b = a; 
    b = [1, 2, 3]; 
    alert(a + ' ' + b); 
} 
why(); 

结果是a=[3,4,5]b = [1,2,3]。为什么它传递的价值? 如何避免参照传递?

+1

严格地说,它总是“按值传递”,并且引用对象的变量的值是对该对象的引用。 JavaScript中不存在“通过引用传递”:https://en.wikipedia.org/wiki/Evaluation_strategy。 –

+0

*“如何避免通过引用传递?”*:您必须复制数组:http://stackoverflow.com/q/7486085/218196。 –

+0

你不“通过引用传递任何东西”。这意味着你会(以伪代码的形式)将函数声明为函数why(const n)或函数why(var n)'。不同之处在于,在第一种情况下,创建了'n'的副本,如果您离开某个函数,它将被销毁,但在第二个函数中您将修改该变量,因此保留该函数将保留您的更改。在你的例子中,'a'和'b'都是全局变量,所以默认情况下它们是引用('why'函数无关)。你应该没有问** ** **(变量引用),** ** **(变量引用)。 – Voitcus

回答

6

JavaScript中非基元变量的值与大多数对象语言一样,是对该对象的引用。

在第二种情况下,您不更改数组,但存储在b中的引用。

如果你要复制你的阵列,使用

var c = a.slice(); 

然后ca将独立发展。

+0

也许添加一个对ES Spec [§8.7](http://es5.github.io/#x8.7) – C5H8NNaO4

+0

@ C5H8NNaO4的引用不会让它更混乱,因为我们这些凡人不能真正使用它参考类型? –

+0

@dystroy如果它是一个多维数组'var a = [1,[1]];'copy'a.slice();'不起作用。 'JSON.parse(JSON.stringify(a))'或者jQuery库扩展'jQuery.extend(true,[],a);'适用于这种情况 – rab

3

因为这就是变量的工作原理。

在第一种情况下,你两个变量设置为一个阵列的相同实例,然后修改该阵列通过bb[0] = 1)。你也可以通过a修改相同的数组,但是点数是ba指向相同的数组; b确实不是“指向”a

在第二,你都设置为一个阵列(b = a),但是然后设置b到一个完全新的,不同的阵列(b = [1,2,3])的一个实例的相同的实例。您正在更改指向b指向的阵列,因为正如我所说,b未指向a,因此它不会影响a,因此它指向相同值a

3

变量ba指向到一个对象。当你做b = // something时,你正在改变变量指向的地方。你没有改变下层的物体。

在第一个代码中,b = a表示ba现在指向相同的对象。因此,当你改变一个时,它会反映在另一个中。

然而,在第二个例子中,你第一指向b到相同的对象a,但然后指向b到一个新阵列([1,2,3])。它在短时间内指向a的事实是无关紧要的。

0

第一种情况是更改数组项的一个值。 b现在等于a [0],b [0]给出相同的值。

在第二种情况下,即使您将b指向a,您为b分配了一个新对象。所以再次b和a是两个不同的对象......如此不同的值。

3

这两种情况都是按价值传递的。 JavaScript是总是传值,没有办法通过引用传递。特别是,传递的值总是一个指向对象的指针。 (实际上,基元直接通过值传递,但差异只能在可变值上观察到,基元是不可变的,所以它没有区别。)

在第一种情况下,指着。但是引用不会改变,只有引用指向的对象。该参考仍然指向同一个对象,该对象只是看起来不同于以前。

这种值传递值的特定情况有时被称为逐个对象共享,逐个对象或共享呼叫,并且是参数传递的方式几乎适用于所有面向对象的语言:Smalltalk,Python,Ruby,Java,C#(默认情况下,可以通过明确指定ref修饰符来传递参考)等。

+0

非常感谢你如果我想改变b [0]的值,但是在第一种情况下不改变[0]的值,我应该怎么做? – user2424844

+0

@ user2424844 ...但不是'a [0]'?然后你需要克隆这个数组。 –

+0

我已经克隆了阵列,但它不起作用 – user2424844

0

毕竟这些很好的答案,没有更多要说的,所以继承人基于ECMAScript 5规范的解释。

在答案的末尾还有一个deepclone函数。

如在ES5规范中定义的,

11.13.1 Simple Assignment (=) 生产AssignmentExpression:

  1. 令LREF是评估LeftHandSideExpression的结果:LeftHandSideExpression = AssignmentExpression如下评价。
  2. 让rref是评估AssignmentExpression的结果。
  3. 让rval为GetValue(rref)
  4. 抛出一个SyntaxError异常,如果以下所有条件:
    • 类型(LREF)为参考是真的
    • IsStrictReference(LREF)为真
    • 类型(GetBase(LREF))是环境记录
    • GetReferencedName(LREF)或者是 “EVAL” 或 “参数”
  5. 呼叫PutValue(lref, rval)
  6. 返回rval。

所以什么时候到达点和rref发生的是一个对象,是
§8.7.1(的在GetValue(V) //V==rref是有趣的部分4b)

4。 如果IsPropertyReference(V),然后

  • (B)返回调用使用碱作为其该值获得内部方法,并传递GetReferencedName(V)为自变量的结果。
  • 在这一点上 rval

现在持有参考的对象,然后把它放在在

凡第8.7.2节(再次PutValue(V,W) //V==lref , W==rval 4b是最有趣的部分)来进场。

注:W是ATM引用您要分配对象和

4. 不然,如果IsPropertyReference(V),然后

  • (B)使用base作为此值调用put内部方法,并为属性名称传递GetReferencedName(V),为值传递W,为Throw标志传递IsStrictReference(V)。

正如你可以在你的情况b这是个大气压参考到对象[6, 7, 8]获取与结果置换看到的,可以这么说GetValue(rref),这是一个参考[1, 2, 3];

然而


显然,你正在寻找一个深克隆功能

这是一个。

Object.defineProperty(Object.prototype, "clone", { 
     value: function (deep) { 
      var type = Object.prototype.toString.call(this).match(/^\[object (.+?)\]$/)[1]; 
      if (type !== "Object") { 
       return this.valueOf; 
      } 

      var clone = {}; 
      if (!deep) { 
       for (var prp in this) { 
        clone[prp] = this[prp]; 
       } 
      } else { 
       for (var prop in this) { 
        if (typeof this[prop] !== "undefined" && this[prop] !== null) 
         clone[prop] = (typeof this[prop] !== "object" ? this[prop] : this[prop].clone((typeof deep == "boolean" ? deep : (deep - 1)))); 
        else 
         clone[prop] = ""; 
       } 
      } 
      return clone; 
     }, 
     enumerable: false 
    }); 
Object.defineProperty(Array.prototype, "clone", { 
     value: function (deep) { 
      var clone = []; 
      if (!deep) clone = this.concat(); 
      else this.forEach(function (e) { 
         if (typeof e !== "undefined" && e !== null) 
          clone.push((typeof e !== "object" ? e : e.clone((deep - 1)))); 
         else 
          clone.push(""); 
        }); 

      return clone; 
     }, 
     enumerable: false 
    }); 

var a = [1, [2, { a: 3 } ], 4]; 
var b = a.clone(Infinity); 

a[1][1]["a"] = "cloned"; 

console.log(a[1][1]["a"], b[1][1]["a"]); //"cloned" , 3 

而一个演示上JSBin

要使用它,只需调用.clone(levelOfDeepness)上的对象或数组

注:我使用的对象原型的纯朴的缘故因为我可以在克隆对象和数组元素时直接调用.clone m(在单个函数中提供了类型检查变体的性能提升)