0

Confused about how increment can change value and that changed value is accessible to getValue, but not to a property of the same object.

Does this have to do with the fact that increment and getValue methods are inner functions of the anonymous function that value is defined in?

var myObject = function() {
    var value = 0;

    return {
        increment: function(inc){
            value += typeof inc === 'number' ? inc : 1;
        },
        getValue: function(){
            return value;
        },
        value: value,
    };
}();

console.log(myObject.getValue()); // 0
myObject.increment(2);
console.log(myObject.getValue()); // 2
console.log(myObject.value); // 0
myObject.increment(2); 
console.log(myObject.getValue()); // 4
  • numbers and strings are copied. – Kevin B Aug 02 '17 at 15:25
  • 2
    `value: value` copies the current value of `value` into the property with the name `value` because it holds a primitive data type, it does not save a reference to it. – t.niese Aug 02 '17 at 15:25
  • This doesn't have anything to do with scoping. The reason `myObject.value` returns the original value is because it's never being incremented. The `value` property of the returned object and the inner `value` variable are two different things, they don't relate to each other in any way apart from the circumstance that the property is created with the value that the inner `value` variable initially has. – Lennholm Aug 02 '17 at 15:25
  • Relevant: https://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language – Kevin B Aug 02 '17 at 15:27
  • 1
    It is as @MikaelLennholm says. `myObject.value` is not the same as `myObject.getValue()`. I believe it is confusing to you because of how you named `var value = 0`, try changing it to: `var helloWorld = 0;`. Now when you are doing `myObject.value` because of `value: helloWorld` means that you **should** get 0. Alternatively when you do `myObject.getValue()` you are now working with the scope of the closure within `myObject` and that is what you are modifying via `myObject.increment(inc)` – AGE Aug 02 '17 at 15:38

2 Answers2

3

var foo creates a variable that is scoped to the function it is declared with in. It can be accessed as foo anywhere inside that function or another function declared inside the first function.

myObject.foo creates a property that is attached to the object. It can be accessed as ???.foo where ??? is a reference to the object that myObject is a reference to. You can do this anywhere you can find a reference to the object.

Properties are not variables. Variables are not properties. (The exception is that global variables are properties of the global (window in a browser) object.


When you create the object you said:

value: value,

But that copies the then current value of the value variable to the value property.

That is a number, which isn't a reference.

When you update the value variable, the value property is unchanged.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • Thanks! Additionally after reading @PatrickRoberts comment below and doing bit of research on closure it is clear what is going on. MyObject.value is just a copy of the value variable declared in the function. The `increment` and `getValue` methods both have access to `value` because they are declared within the function that defines `value`. This access to `value` is because of `closure`. – user2731105 Aug 02 '17 at 19:16
  • To add a bit more detail, the methods have access to the context in which they were created. This means that they have access to the original variable, and not a copy of the variable. – user2731105 Aug 03 '17 at 17:54
2

Other answers like @Quentin's have explained what the issue is pretty thoroughly. Here's a proper way by minimally modifying your current approach to correct the problem though:

var myObject = function() {
  return {
    increment: function(inc) {
      this.value += typeof inc === 'number' ? inc : 1;
    },
    getValue: function() {
      return this.value;
    },
    value: 0
  };
}();

console.log(myObject.getValue()); // 0
myObject.increment(2);
console.log(myObject.getValue()); // 2
console.log(myObject.value); // 2
myObject.increment(2);
console.log(myObject.getValue()); // 4
console.log(myObject.value); // 4

However, this functionality would be a great example to make use of an ES6 class:

class Counter {
  constructor (value = 0) {
    this.value = value
  }
  
  increment (amount = 1) {
    this.value += amount
  }
  
  getValue () {
    return this.value
  }
}

let myObject = new Counter()

console.log(myObject.getValue()) // 0

myObject.increment(2)

console.log(myObject.getValue()) // 2
console.log(myObject.value) // 2

myObject.increment(2)

console.log(myObject.getValue()) // 4
console.log(myObject.value) // 4

As was pointed out in comments, the closure and getValue() seem rather pointless. If you want to allow value to access a scoped variable, and disable modifying it, you could implement it this way:

var myObject = function() {
  var value = 0;

  return {
    increment: function(inc) {
      value += typeof inc === 'number' ? inc : 1;
    },
    getValue: function() {
      return value;
    },
    get value() {
      return value;
    },
    // disable setting it without throwing
    set value(newValue) {
      return value;
    }
  };
}();

console.log(myObject.getValue()); // 0

myObject.increment(2);

console.log(myObject.getValue()); // 2
console.log(myObject.value); // 2

// won't work
myObject.value = 4

// still 2
console.log(myObject.getValue()); // 2
console.log(myObject.value); // 2
Community
  • 1
  • 1
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • 1
    You beat me to the answer. Took too long. – Doug Coburn Aug 02 '17 at 15:39
  • This also gives complete public access to the `value`, which kind of makes the `getValue()` function pointless – Lennholm Aug 02 '17 at 15:42
  • @MikaelLennholm well, yes, but perhaps there's an underlying reason this simplified example is used. We don't know if the OP has a more complicated program that has been simplified to this, so I don't assume anything. – Patrick Roberts Aug 02 '17 at 15:45
  • @MikaelLennholm you did inspire me to give yet another alternative approach. See my update – Patrick Roberts Aug 02 '17 at 15:58
  • This is a somewhat modified example from the closure chapter in JavaScript: The Good Parts, so this is purely a learning exercise. With that said... what is `value` that `increment` and `getValue` are pointing to? – user2731105 Aug 02 '17 at 17:55
  • @user2731105 in which example? 1, 2 or 3? – Patrick Roberts Aug 02 '17 at 17:56
  • @PatrickRoberts, in my original post. Confused about how `increment` can change `value` and that changed `value` is accessible to `getValue`, but not to a property of the same object. I'll edit my response to clarify this question as well. – user2731105 Aug 02 '17 at 18:00
  • @user2731105 in your original post, you have two separate `value`'s, the one in the scope declared `var value = 0`, and the copy you created in the returned object, `value: value`. When `getValue()` and `increment()` were invoked, they both referenced the original scoped `value`, while `myObject.value` referred to the copy made on the returned object literal, which remained `0` since the two functions only accessed and modified the original `value` and not the property on the returned object. – Patrick Roberts Aug 02 '17 at 18:02