1

Arrays are passed by reference in javascript. That means arrays are mutable and can be changed in called function. Why am I not able to Re-initialization an array in called function?

This is also true for python

function calling(){
    var arr = [1,2]
    console.log(arr); //prints [1,2]
    called(arr);
    console.log(arr); //prints [1,2,3,4],but I was expecting [5,6]

}

function called(arr){
    arr.push(3)
    arr.push(4)
    arr = [5,6]
}

calling()

UPDATE I am seeking justification of (mutability, pass by reference, pass by assignment and re-initialization of an array) Please don't post workaround and solution of the problem. I know how to get arr to print [5,6]

Praveen Singh Yadav
  • 1,831
  • 4
  • 20
  • 31
  • Javascript or Python? Which are you asking about? Your code looks like Javascript. Please fix tags to just be one. – jfriend00 Jan 10 '16 at 16:54
  • 1
    A little unsure why you cross-posted here: http://programmers.stackexchange.com/questions/307030/re-initialization-array-in-called-function – jfriend00 Jan 10 '16 at 16:55
  • Instead of `arr = [5,6]`, try `arr.splice(0, arr.length, 5, 6)` – thefourtheye Jan 10 '16 at 16:55
  • @jfriend00 : this is more of of sudo code. The case holds for both – Praveen Singh Yadav Jan 10 '16 at 17:08
  • @jfriend00 my bad. I will take programmer.stackexchange one down. – Praveen Singh Yadav Jan 10 '16 at 17:09
  • @jfriend00: although the answer is actually the same for both: both are strictly pass-by-value, that's why the code assuming they are pass-by-reference doesn't work. – Jörg W Mittag Jan 10 '16 at 17:21
  • 1
    @JörgWMittag - yeah, pass by value, but the value is a pointer or reference. Unfortunately, that technical explanation rarely helps newbies actually understand how/why it works. This general question plays out every few weeks here and we see over and over what types of explanation actually help newbies understand it. Just saying everything is "pass by value" never solves the mystery one bit for a newbie who doesn't yet grasp this concept. One needs a lot more explanation than that in simpler terms. – jfriend00 Jan 10 '16 at 17:24
  • @jfriend00: actually, it seems to me from both the question and the comments that the OP *does* understand the definitions of pbv and pbr. The problem seems rather simple: someone told the OP that JS was pbr, he wrote code under that assumption, the code doesn't work, now he wants to know why, and the simple answer is: because JS is pbv, not pbr. – Jörg W Mittag Jan 10 '16 at 17:26
  • @JörgWMittag - If you are so sure that is the answer the OP wants, then write an answer that says just that and see if that's what the OP likes best. That's what answers are for. You shouldn't be arguing for an answer in a comment. – jfriend00 Jan 10 '16 at 17:28
  • @jfriend00: Simple question. is pass by value same as passing a copy of variable? – Praveen Singh Yadav Jan 10 '16 at 17:29
  • A quick search shows that pbv maked copy of var.(Please correct me if I am wrong). http://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value. In this case, the array shouldn't be mutable. – Praveen Singh Yadav Jan 10 '16 at 17:33
  • 1
    It's not quite that simple. In Javascript, when you have `arr = [1,2]`, what `arr` contains is a reference to an array object. When you do `foo(arr)`, you are passing a copy of that reference so now you have two copies of the same reference. Each one points at the same object. Neither points at each other. So, "yes" it is technically pass by value, but you're passing a reference to the array which is a copy of the reference that was in `arr`, not a reference to the variable itself. I logically think of it more like "pass by pointer" and find that less confusing. – jfriend00 Jan 10 '16 at 17:40
  • JörgWMittag , jfriend00 .Much appreciated. Thanks for sharing so many pointers. Although it would take me some time to fully understanding your answers. I will get profound knowledge about it. . – Praveen Singh Yadav Jan 10 '16 at 18:17

3 Answers3

4

Arrays are passed by reference in javascript.

No, they aren't. ECMAScript/JavaScript is strictly pass-by-value. (More precisely, call-by-sharing, which is a special case of pass-by-value.)

That means arrays are mutable and can be changed in called function.

No, that's not what it means. It means exactly what it says: that the reference within the caller's scope to the array is passed as an argument into the function, and not the value.

Whether or not the array is mutable or not has nothing to with pass-by-reference vs. pass-by-value. ECMAScript is not a purely functional language, most objects can be mutated. Numbers, Symbols, and Strings are an exception.

Why am I not able to Re-initialization an array in called function?

You are trying to modify the reference within the caller's scope. That only works with pass-by-reference. ECMAScript is not pass-by-reference, whoever told you that is simply wrong.

This is also true for python

Python behaves identically to ECMAScript in this regard, yes, it is also pass-by-value.

Your confusion stems from the fact that you erroneously believe ECMAScript/JavaScript is pass-by-reference, when in fact it is not.

ECMAScript uses pass-by-value, or more precisely, a special case of pass-by-value where the value being passed is always a pointer. This special case is also sometimes known as call-by-sharing, call-by-object-sharing or call-by-object.

It's the same convention that is used by Java (for objects), C# (by default for reference types), Smalltalk, Python, Ruby and more or less every object-oriented language ever created.

Note: some types (e.g. Numbers) are actually passed directly by value and not with an intermediary pointer. However, since those are immutable, there is no observable behavioral difference between pass-by-value and call-by-object-sharing in this case, so you can greatly simplify your mental model by simply treating everything as call-by-object-sharing. Just interpret these special cases as internal compiler optimizations that you don't need to worry about.

Here's a simple example you can run to determine the argument passing convention of ECMAScript (or any other language, after you translate it):

function isEcmascriptPassByValue(foo) {
  foo.push('More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value!');
  foo = 'No, ECMAScript is pass-by-reference.';
  return;
}

var bar = ['Yes, of course, ECMAScript *is* pass-by-value!'];

isEcmascriptPassByValue(bar);

console.log(bar);
// Yes, of course, ECMAScript *is* pass-by-value!,
// More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value!
def is_python_pass_by_value(foo):
    foo[0] = 'More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value!'
    foo = ['Python is not pass-by-reference.']

quux = ['Yes, of course, Python *is* pass-by-value!']

is_python_pass_by_value(quux)

print(quux[0])
# More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value!

If you are familiar with C#, it is a very good way to understand the differences between pass-by-value and pass-by-reference for value types and reference types, because C# supports all 4 combinations: pass-by-value for value types ("traditional pass-by-value"), pass-by-value for reference types (call-by-sharing, call-by-object, call-by-object-sharing as in ECMAScript), pass-by-reference for reference types, and pass-by-reference for value types.

(Actually, even if you don't know C#, this isn't too hard to follow.)

// In C#, struct defines a value type, class defines a reference type
struct MutableCell
{
    public string value;
}

class Program
{
    // the ref keyword means pass-by-reference, otherwise it's pass-by-value
    // You must explicitly request pass-by-reference both at the definition and the call
    static void IsCSharpPassByValue(string[] foo, MutableCell bar, ref string baz, ref MutableCell qux)
    {
        foo[0] = "More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.";
        foo = new string[] { "C# is not pass-by-reference." };

        bar.value = "For value types, it is *not* call-by-sharing.";
        bar = new MutableCell { value = "And also not pass-by-reference." };

        baz = "It also supports pass-by-reference if explicitly requested.";

        qux = new MutableCell { value = "Pass-by-reference is supported for value types as well." };
    }

    static void Main(string[] args)
    {
        var quux = new string[] { "Yes, of course, C# *is* pass-by-value!" };

        var corge = new MutableCell { value = "For value types it is pure pass-by-value." };

        var grault = "This string will vanish because of pass-by-reference.";

        var garply = new MutableCell { value = "This string will vanish because of pass-by-reference." };

        // the first two are passed by value, the other two by reference
        IsCSharpPassByValue(quux, corge, ref grault, ref garply);

        Console.WriteLine(quux[0]);
        // More precisely, for reference types it is call-by-object-sharing, which is a special case of pass-by-value.

        Console.WriteLine(corge.value);
        // For value types it is pure pass-by-value.

        Console.WriteLine(grault);
        // It also supports pass-by-reference if explicitly requested.

        Console.WriteLine(garply.value);
        // Pass-by-reference is supported for value types as well.
    }
}
Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
3

If you want to get technical, Javascript is really only "pass by value" - it is not "pass by reference". When you pass an object (like an Array), you are passing by value and the value is a reference (a copy of a reference to the array, not a copy of the actual array). This means you can use the value reference to mutate the original object, but it's not an actual reference to the original variable so you can't affect the contents of the original variable itself (e.g. you can't cause that other variable to point at a new object).

Since this language jargon doesn't actually explain what is going on for many folks, what follows here is more of a description of how it works:


When you do:

arr = [5,6]

inside your function, you are just assigning a new array to the arr function argument. You've just instructed Javascript to take the arr argument variable and point it at a new array object. That has no effect at all on the original array object that was passed into the function. It still happily exists in its other life.

You could change that array like this:

function called(arr){
    arr.push(3)
    arr.push(4)
    // now set arr back to [5,6]
    arr[0] = 5;
    arr[1] = 6;
    arr.length = 2;
}

It's a bit of a misnomer to say arrays are passed by reference because it isn't really a full reference like you would find in C++, for example. Some people say it's "pass by sharing" or "pass by pointer". Some people even call it "pass by value, but the value is a reference" which is probably technically correct, but doesn't usually help newbies understand what is going on. In my brain (partly because I know C/C++), I think of it like "pass by pointer".

Though Javascript doesn't actually have it's own data type that is a pointer, passing an array works more like passing a pointer works in C++. If you index off the pointer, you are accessing the original object:

arr[0] = 5;    // this changes the original object

But, if you reassign the pointer a new value:

arr = [5,6];   // this just makes arr point at a new and different array

Then, you are just making the pointer variable itself point at a new object and the original object still remains untouched.


So, in summary. If you reference arr as in arr.push(x), then you are mutating the original array object.

But, if you assign arr a new array as in arr = [5,6], then you are just telling Javascript that you want the arr variable to point at a new array. Your arr variable in that function now points at a different array than it did before. The original array can no longer be reached by this code. But any other code that was pointing at that original array is still pointing at that original array. If you know C/C++, it actually works fairly similarly to a pointer in C++.


Here's a simpler example that is outside of the context of a function argument:

var x = [1,2];
var y = x;
console.log(x);    // [1,2]
console.log(y);    // [1,2]

y = [3,4];
console.log(x);    // [1,2]
console.log(y);    // [3,4]

You've merely asked y to now point to a new array [3,4]. Assigning y to point to a new array does not affect the original array at all.

In this example, the variable y is like the arr function argument in your called() function. Assigning a new value to it doesn't change the original in any way.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I added some info to the start of my answer about how JS is technically pass by value, not pass by reference. But for an object, the value that is passed is a reference to the object itself, not a reference to the original variable. – jfriend00 Jan 10 '16 at 17:41
1

In Python:

In [170]: def called(arr):
   .....:     arr.append(3)
   .....:     arr.append(4)
   .....:     arr=[5,6]
   .....:     
In [171]: arr=[1,2]
In [172]: called(arr)
In [173]: arr
Out[173]: [1, 2, 3, 4]

Initially in called, arr is a reference to the mutable list [1,2]. The 2 append calls modify that list. But the arr=[5,6] step reassigns the variable. The link to outside arr is broken, so there's no further change.

If I'd added a return arr statement to the call,

In [175]: called(arr)
Out[175]: [5, 6]
In [176]: arr
Out[176]: [1, 2, 3, 4, 3, 4]

I modify the global arr again, but return the [5,6].

called would be clearer if written:

def called(arr):
    arr.append(3)
    arr.append(4)
    res=[5,6]
    return res

The last 2 lines have nothing to do with initial arr argument, so there is not point in reusing the variable name.

For further illustration of what is going on, look at the object id at various points:

In [178]: def called(arr):
    arr.append(3)
    arr.append(4)
    print(id(arr))
    arr = [5,6]
   .....:     return arr
   .....: 
In [179]: id(arr)
Out[179]: 2999818796
In [180]: id(called(arr))
2999818796             # id of global arr
Out[180]: 2999999564   # id of returned list
In [181]: id(arr)
Out[181]: 2999818796
hpaulj
  • 221,503
  • 14
  • 230
  • 353