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

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

The result is a[0]=1, b[0]=1;. It seem likes JavaScript is passing by reference?

But in this case:

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

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

The result is a=[3,4,5] and b = [1,2,3]. Why is it passing by value? How to avoid passing by reference?

  • 1
    Strictly speaking it's always "pass by value" and the value of variables referring to objects is a reference to the object. "Pass by reference" does not exist in JavaScript: https://en.wikipedia.org/wiki/Evaluation_strategy. – Felix Kling May 27 '13 at 11:52
  • *"How to avoid passing by reference?"*: You have to copy the array: http://stackoverflow.com/q/7486085/218196. – Felix Kling May 27 '13 at 12:02
  • You do not "pass anything by reference". That means that you would (in pseudocode) declare your function as `function why(const n)` or `function why(var n)`. The difference is that in the first case the copy of `n` is made and if you leave a function it is destroyed, but in the second you modify the variable, so leaving the function will keep your changes. In your examples both `a` and `b` are global variables, so by default they are references (the `why` function has nothing to do). You should have asked not about **passing** (variables by reference) but **being** (the variables references). – Voitcus May 27 '13 at 12:10

6 Answers6

6

The value of a non primitive variable, in JavaScript like in most object languages, is a reference to the object.

In the second case, you're not changing the array, but the reference that is stored in b.

If you want to copy your array, use

var c = a.slice();

Then c and a will evolve independently.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • Maybe add a reference to ES Spec [§8.7](http://es5.github.io/#x8.7) – Moritz Roessler May 27 '13 at 12:14
  • @C5H8NNaO4 Woudln't it make it more confusing, as we mere mortals can't really use this reference type ? – Denys Séguret May 27 '13 at 12:16
  • @dystroy if it's a multidimenional array `var a = [ 1 ,[1]];` copy `a.slice();` not works . `JSON.parse( JSON.stringify( a ) )` or jQuery library extend `jQuery.extend( true, [], a );` works for this case – rab May 27 '13 at 12:16
  • @rab It depends of what you mean by "not works". `slice` does make a copy of the array. What it doesn't make is a *deep* copy. – Denys Séguret May 27 '13 at 12:17
  • @dystroy but it describes the process pretty well. What do you mean by can't use this reference type? Isn't it part of an [Simple Assignment](http://es5.github.io/#x11.13.1) – Moritz Roessler May 27 '13 at 12:23
  • Well, yes, but you can't pass this reference. But this type is one more reason not to use the word `pointer`, which might be a clear image but is abusive. – Denys Séguret May 27 '13 at 12:25
  • slice just work on a simple array, not work in this case: a=[[[1,2],3],4,5] b = a.slice(0) b[0][0][0]=0 alert(a[0][0][0]+' '+b[0][0][0]) – user2424844 May 27 '13 at 13:08
  • @user2424844 If you want a deep copy, do as was suggested by rab : stringify then parse. But it's heavy and normally almost never needed in a real application. – Denys Séguret May 27 '13 at 13:40
3

Because that's how variables work.

In the first case, you're setting both variables to the same instance of one array, then modifying that array through b (b[0] = 1). You could also modify the same array through a, but the point is b and a point to the same array; b does not "point" to a.

In the second, you're setting both to the same instance of one array (b = a) but then setting b to an instance of an entirely new, different array (b = [1,2,3]). You're changing the array that b points to, you're not affecting a because, as I said, b didn't point to a, it pointed to the same value as a.

user229044
  • 232,980
  • 40
  • 330
  • 338
3

The variables b and a are pointing to an object. When you do b = // something, you're changing where the variable points to. You're not changing the underlaying object.

In the first code, b = a means that b and a are now pointing to the same object. Therefore, when you make a change to one, it's reflected in the other.

However, in the second example, you're first pointing b to the same object as a, but then pointing b to a new array ([1,2,3]). The fact it was pointing to a for a short amount of time is irrelevant.

Matt
  • 74,352
  • 26
  • 153
  • 180
3

Both cases are pass-by-value. JavaScript is always pass-by-value, there is no way to pass by reference. In particular, the value being passed is always a pointer to an object. (Actually, primitives are passed by value directly, but the difference can only be observed for mutable values and primitives are immutable, so it doesn't make a difference.)

In the first case you are mutating the object the pointer points to. But the reference doesn't get changed, only the object the reference points to. The reference still points to the same object, the object just looks different than it did before.

This specific case of pass-by-value-where-the-value-is-a-pointer is sometimes called call-by-object-sharing, call-by-object or call-by-sharing, and is the way argument passing works in pretty much all object-oriented languages: Smalltalk, Python, Ruby, Java, C# (by default, you can pass-by-reference by explicitly specifying the ref modifier) etc.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
0

In first case are changing one value of array item. b now equals to a so a[0] and b[0] gives same value.

In second case even though you made b point to a, you assigned a new object to b. so again b and a are two different objects ...so different values.

Guanxi
  • 3,103
  • 21
  • 38
0

After all these great Answers, there is not really more to say, so heres an explanation based on the ECMAScript 5 specification.

Also a deepclone function at the end of the Answer.

As defined in the ES5 specification,

11.13.1 Simple Assignment ( = ) The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:

  1. Let lref be the result of evaluating LeftHandSideExpression.
  2. Let rref be the result of evaluating AssignmentExpression.
  3. Let rval be GetValue(rref).
  4. Throw a SyntaxError exception if the following conditions are all true:
    • Type(lref) is Reference is true
    • IsStrictReference(lref) is true
    • Type(GetBase(lref)) is Environment Record
    • GetReferencedName(lref) is either "eval" or "arguments"
  5. Call PutValue(lref, rval).
  6. Return rval.

So whats happening when we reach point 3 and rref is an Object, is
§8.7.1 (section 4b of In GetValue(V) //V==rref is the interesting point)

4. If IsPropertyReference(V), then

  • (b) Return the result of calling the get internal method using base as its this value, and passing GetReferencedName(V) for the argument.

Now at this point rval holds the reference to the Object, which then gets put in at 5.

Where §8.7.2 (again 4b of PutValue(V,W) //V==lref , W==rval is the interesting part) comes into play.

Note: W is atm the reference to the Object you want to assign and not the value

4. Else if IsPropertyReference(V), then

  • (b) Call the put internal method using base as its this value, and passing GetReferencedName(V) for the property name, W for the value, and IsStrictReference(V) for the Throw flag.

As you can see in your case the value of b which is atm a reference to the Object [6, 7, 8] gets replaced with the result of, so to speak GetValue(rref), which is a reference to [1, 2, 3];

However


Apparently you are looking for a deep clone function

Here is one.

 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

And a Demo on JSBin

To use it just call .clone(levelOfDeepness) on an Object or Array

Note: I used the Objects prototype for simplicities sake as i can call directly .clone of Object and Array elements while cloning them (gives a performance boost over the type checking variant in a single function)

Moritz Roessler
  • 8,542
  • 26
  • 51