2

I apologize if this is a noob question, I'm still kind of new to JS, Why doesn't overwriting a variable work inside a function?

var o = {x: 1};

function foo(){
    // Overwrite var `o` with a string
    arguments[0] = "string";
}

foo(o);

console.log(o); // Why is `o` still an object and not the string `string`?

-- Update based on responses ---

But why does this then work?

// Define an array literal
var a = ["a", "b"];

// Execute whatever function is passed to manipulate the array
a.execFunc = function(func){
    func(this) // Pass this array (object) to it
}

// Invoke `execFunc` function passing another function
a.execFunc(function(thisArray){
    thisArray[0] = "c"; // Change first element to "c"
})

console.log(a); // => ["c", "b", execFunc: function]: IT WORKED! a → c

--- Update #2 ----

Sorry just trying to wrap my mind around what is being said - why then DOESN'T this work? Isn't it basically the same as above? I'm modifying the original array just like above, not an arguments object, aren't I?

// Define an array literal
var a = ["a", "b"];

// Execute whatever function is passed to manipulate the array
a.execFunc = function(func){
    func(this[0]) // Pass the first element (object property) to it
}

// Invoke `execFunc` function passing another function
a.execFunc(function(firstElement){
    console.log(firstElement = "c"); // Change first element to "c"
})

console.log(a); // => ["a", "b", execFunc: function]: first element is still `a`
user1019031
  • 743
  • 7
  • 16
  • 1
    Everything is passed by value is JS. `arguments[0]` is not a reference to `o`, it is a new value, that holds a reference to `o`. – elclanrs Mar 06 '15 at 07:51
  • I answered a similar question today already: http://stackoverflow.com/a/28889626/218196. – Felix Kling Mar 06 '15 at 07:54
  • The difference is that in first case you mutating `arguments`, a value that **implicitly** exists in every function. However, because `arguments` is not accessible *outside* of the function, you cannot observe its mutation outside the function. In the second case you are mutating an existing array defined by yourself. – Felix Kling Mar 06 '15 at 07:57
  • When you are using `=` operator, you are actually just changing what the variable points to. So, `firstElement = "c"` just made `firstElement` refer the object `"c"` instead of `a`. That's it. – thefourtheye Mar 06 '15 at 08:10
  • hm? So in the 1st example, I understand that the `arguments[0]` array is just a reference to `o` object and only modifies that reference. For the 2nd example, I am passing a _reference_ to `a` array, and then modifying `referenceToArray.actualValue`, so it works. For the 3rd example, I am passing `referenceToArray.referenceToValue` and overwriting that reference therefore it doesn't work? Is that right? – user1019031 Mar 06 '15 at 08:22
  • no, in 1st you just pass the value of your object and modify arguments[0], you did not modify the reference of o object. – Sing Mar 06 '15 at 08:28
  • 1
    @user1019031 First of all, why do you have two functions in the third example? Keep it simple. Try to shorten it with one function. You might be able to understand it better. – thefourtheye Mar 06 '15 at 08:28
  • @thefourtheye agree, if you are new to js, just be simple as you can understand. – Sing Mar 06 '15 at 08:29
  • I realize the examples I gave are a bit contrived, I guess I'm having a hard time articulating a clear 'rule' in my head. e.g. all arguments in functions are local variables that serve as references (so not actual 'whatever was passed'), but accessor properties of references are actual values? – user1019031 Mar 06 '15 at 08:30
  • @thefourtheye I was playing around with writing a function similar to `Array.forEach(_anotherFunc_)` to understand how a function can be passed as an argument to another function and what the implications might be. (i.e. if `this` would lose its binding, etc.) – user1019031 Mar 06 '15 at 08:33
  • maybe you could refer to my new update, I think it is clearer than my original one :) – Sing Mar 06 '15 at 08:47
  • `thisArray[0] = "c";` **reads** the value of `thisArray` and **mutates** the array itself. It does *not* assign a new value to `thisArray`. Any other variable that references the same array (i.e. `a`) will "see" the mutated array. `firstElement = "c"` **writes** to `firstElement`. It does *not* mutate a value, it assigns a new value to the variable `firstElement`. As I already mentioned, I answered as similar question earlier: http://stackoverflow.com/a/28889626/218196. The same applies to parameters/arguments. – Felix Kling Mar 06 '15 at 09:41
  • possible duplicate of [Is JavaScript a pass-by-reference or pass-by-value language?](http://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language) – Qantas 94 Heavy Mar 06 '15 at 11:38

2 Answers2

3

When you are passing an object as an argument to a function, the arguments special object will hold a reference to the actual object. When you do

arguments[0] = ...

you are simply making the element at 0 refer a different object, you are not changing o. You can understand this better, if you try this

function foo() {
    console.log(arguments);
    arguments[0] = "string";
    console.log(arguments);
}

The output will be

{ '0': { x: 1 } }
{ '0': 'string' }

as you can see, the element at 0 earlier was referring to o and that has been changed to string. That's it.

Instead, try this

function foo() {
    arguments[0].x = "string";
}

you will get

{ x: 'string' }

when you print o, because, arguments[0] refers to o and the .x refers to a property in o. So you are actually changing o. This is called mutating an object.

In the same way, thisArray refers to your actual array object and when you change the element at 0, you are actually changing your actual array passed as the argument. That is why mutations in thisArray reflects in your actual array object as well.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
1

If you change the object's 'property' in function, javascript will find the reference and change it.

If you just change object's 'value', javascript will treat it as a local variable instead get the reference and modify it.

In first one, you didn't change the property of o, so it just overwrite argument[0], and didn't set anything to variable o. You need to return new value in function and set to o, should be like this:

var o = {x: 1};

function foo(){
    // Overwrite var `o` with a string
   arguments[0] = "string";
   return arguments[0];
}

o = foo(o);

console.log(o);
//will be "string"

Or

var o = {x: 1};

function foo(){
    // Overwrite var `o` with a string
    o = "string";
}

foo();

console.log(o);
//will be "string"

Your second one is like this:

var a = ["a","b"];

function foo(){
    // Overwrite var `o` with a string
    arguments[0][0] = "c";
}

foo(a)

console.log(a)
//will be ["c", "b"]

You tend to change property of array a. So it refer to a array. But in your first sample, you just change its value so it will not refer to o object.

Your update #2

You just change the "value" of a[0]. So it will not refer to array a. Similar as this:

var a = ["a", "b"];

function foo(){

    arguments[0] = "c";
}

foo(a[0]);

console.log(a);
//will be ["a", "b"]
Sing
  • 3,942
  • 3
  • 29
  • 40