为什么JavaScript通过引用传递?

时间:2013-05-27 11:48:29

标签: javascript

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是通过引用传递的?

但在这种情况下:

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]。它为什么通过价值? 如何避免通过引用传递?

6 个答案:

答案 0 :(得分:6)

与大多数对象语言一样,非原始变量的值是对象的引用。

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

如果要复制阵列,请使用

var c = a.slice();

然后ca将独立发展。

答案 1 :(得分:3)

因为这就是变量的工作方式。

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

在第二个中,您将两者都设置为一个数组的同一个实例(b = a),然后将b设置为全新的实例,不同数组(b = [1,2,3])。您正在更改b指向的数组,但不会影响a,因为正如我所说,b未指向a,它指向a相同的值

答案 2 :(得分:3)

变量ba 指向到对象。执行b = // something时,您正在更改变量指向的位置。你没有改变底层物体。

在第一个代码中,b = a表示ba现在指向同一个对象。因此,当您对一个进行更改时,它会反映在另一个中。

但是,在第二个示例中,您首先将b指向与a相同的对象,但然后b指向 new 数组([1,2,3])。它在短时间内指向a这一事实无关紧要。

答案 3 :(得分:3)

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

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

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

在所有这些伟大的答案之后,没有更多的话要说,所以继承了基于ECMAScript 5规范的解释。

在答案结尾处也是一个深层克隆功能。

如ES5规范中所定义,

11.13.1 Simple Assignment ( = ) 生产AssignmentExpression:LeftHandSideExpression = AssignmentExpression的计算方法如下:

  1. 让lref成为评估LeftHandSideExpression的结果。
  2. 让rref成为评估AssignmentExpression的结果。
  3. 让rval为 GetValue(rref)
  4. 如果满足以下条件,则抛出SyntaxError异常:
    • Type(lref)是Reference is true
    • IsStrictReference(lref)为真
    • 类型(GetBase(lref))是环境记录
    • GetReferencedName(lref)是“eval”或“arguments”
  5. 致电 PutValue(lref, rval)
  6. 返回rval。

  7. 当我们到达点 3 并且rref是一个对象时,发生了什么事,是 §8.7.1(In GetValue(V) //V==rref的4b节是有趣的观点)

    4。  如果是IsPropertyReference(V),那么

    • (b)返回使用base作为此值调用get internal方法的结果,并为参数传递GetReferencedName(V)。

    现在此时rval引用保存到对象,然后将其放入 5。

    其中§8.7.2(PutValue(V,W) //V==lref , W==rval的4b再次是有趣的部分)开始发挥作用。

    注意:W是对您要分配的对象的引用,

    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(在单个函数中提供类型检查变量的性能提升)< / em>的