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

但在这种情况下:

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

4

6 回答 6

6

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

在第二种情况下,您不会更改数组,而是更改存储在b.

如果要复制数组,请使用

var c = a.slice();

然后c并将a独立发展。

于 2013-05-27T11:51:36.300 回答
3

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

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

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

于 2013-05-27T11:51:46.090 回答
3

变量b指向a一个对象。当你这样做时,你正在改变变量指向的位置。您没有更改底层对象。b = // something

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

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

于 2013-05-27T11:52:24.657 回答
3

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

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

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

于 2013-05-27T11:53:37.367 回答
0

在所有这些很棒的答案之后,真的没有更多可说的了,所以这里有一个基于 ECMAScript 5 规范的解释。

答案末尾还有一个深度克隆功能。

正如 ES5 规范中定义的那样,

11.13.1 简单赋值 (=) 产生式 AssignmentExpression : LeftHandSideExpression = AssignmentExpression 计算如下:

  1. 令 lref 为评估 LeftHandSideExpression 的结果。
  2. 令 rref 为评估 AssignmentExpression 的结果。
  3. 设 rval 为GetValue(rref)
  4. 如果以下条件都为真,则抛出 SyntaxError 异常:
    • 类型(lref)是参考是真的
    • IsStrictReference(lref) 为真
    • Type(GetBase(lref)) 是环境记录
    • GetReferencedName(lref) 是“eval”或“arguments”
  5. 打电话PutValue(lref, rval)
  6. 返回 rval。

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

4. 如果 IsPropertyReference(V),那么

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

现在此时rval持有对 Object 的引用,然后将其放入5。

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

注意:W 是 atm 对要分配的对象的引用,而不是

4. else if IsPropertyReference(V),则

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

正如您在您的情况下看到的那样,它的值batm对 Object的引用[6, 7, 8]被替换为可以说的结果GetValue(rref),这是对Object 的引用[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的Demo

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

注意:为了简单起见,我使用了 Objects 原型,因为我可以.clone在克隆它们时直接调用 Object 和 Array 元素(比单个函数中的类型检查变体提高性能)

于 2013-05-27T13:57:58.380 回答
0

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

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

于 2013-05-27T11:53:15.000 回答