1

I'm doing some programming in node these days and I'm kinda surprised by the handling of scope in functions. I was led to believe that now with ES6 scope is a lot stricter. The following works as expected:

function f(v){
    v += 1;
    return v;
}
let before = 1;
let after = f(before);
console.log(after);  // This logs 2 (as expected)
console.log(before);  // This logs 1 (as expected)

But when I do the same using a object/dictionary, the scope of the variable seems to go outside the function:

function f(v){
    v.a += 1;
    return v;
}
let before = {a: 1};
let after = f(before);
console.log(after.a);  // This logs 2 (as expected)
console.log(before.a);  // This also logs 2 (I was expecting this to still be 1)

Why on earth is this the case? Why is the scope of v limited to the function when it's an int, but not when it's an object?

kramer65
  • 50,427
  • 120
  • 308
  • 488
  • This seems like a case of primitive data types vs. objects. A similar thing would happen in Java with ints vs. Strings – abagh0703 Mar 15 '18 at 09:13
  • In JS, only string and number are passed by value, anything else are passed by reference / pointer. It has nothing to do with ES6. – FisNaN Mar 15 '18 at 09:17
  • @t.niese Something like that. Objects are not copied. But what does get copied, is a reference to those objects. So, `before` and `after` are different references, but they have the same **value**: address of the object itself. – Zlatko Mar 15 '18 at 09:25
  • Or something like that :) – Zlatko Mar 15 '18 at 09:26
  • 1
    @FisNaN You need to take care if you use the term _reference_ and _pointer_. Pass by reference does not really exists in js, at least not in the sens that `function test(a) { a = {foo:1} } test(b); console.log(b.foo)` would work. – t.niese Mar 15 '18 at 09:26
  • 1
    @Zlatko Yes I know. But I didn't have the time (or lets to be honest the motivation) to write an answer with the correct terms. :) And as of that it was just a comment. But your answer explains it well. – t.niese Mar 15 '18 at 09:30
  • @t.niese I see. But the functions in the question use typically side-effect pattern, which should be avoid anyway. – FisNaN Mar 15 '18 at 09:41

3 Answers3

1

There is nothing wrong with scopes here. Your code is simply assigning an object to another variable, so whenever you edit your object, all assigned variables will be affected :

let o1 = {
  a: 1
};
let o2 = o1;

o1.a++;
console.log(o2);
Zenoo
  • 12,670
  • 4
  • 45
  • 69
1

Javascript has two data types: primitive and Object.

In your first example, you're passing in a primitive data type (Boolean, Null, Undefined, String, Symbol, and Number - your case). When you pass these types of variables into a function, you're passing a copy - so anything modified does not affect the initial value/variable, as in your first case.

With the Object data type, when you pass an object into a function, you're passing in a reference to that object's value - not a copy of that value, as with primitives. So when you modify this in your function, you're modifying the reference. This means that any variables that have this reference will have the modified value as well, since they're just pointing to a value that has recently been modified.

abagh0703
  • 841
  • 1
  • 12
  • 26
1

JavaScript functions pass all arguments by value.

In the first case, you have a primitive value (number is primitive). So, before refers to a primitive, value 1. You pass it in - it's passed by value. You return a value. Assign it to the second object. after is another object. Basically, two pointers, to different objects. Different objects. Different values.

Think of it in terms of garbage collection. You lose "before" out of your scope. That 1 primitive is no longer accessible. Free to clean up.

In the second case, you pass a value of the object reference. And you return again a value of an object reference. So before and after refer to the SAME OBJECT. They are different references. So, in both console.log(before) and console.log(after), you refer to the same object and the same (now modified) value.

Now, again, look at the garbage collector. Remove access to before. Garbage collector removes it's value (which is, basically, a pointer to the object {v: 2}). Can it remove the object itself? Nope, not yet, because the value of after still points to this object. But regardless of that, both vars point to the same object.

Zlatko
  • 18,936
  • 14
  • 70
  • 123