4

I'm exporting a factory function in ES6, which returns an object with properties. When calling the factory, the object is created, but its values don't update.

Example:

// factory.js
let counter = 1;
let factory = () => {
    let increment = function(){
      counter++;
    }

  return { counter, increment };
}

export default factory;


// main.js
import factory from './factory';

let f = factory();
console.log(f.counter); // =>1
f.increment();
console.log(f.counter); // => stil 1, not 2?

Can someone explain why this is happening? Is it an ES6 feature, or something to do with the fact that I am using webpack and babel (es2015-preset)?

I have found this, but that doesn't describe the same use-case: http://exploringjs.com/es6/ch_modules.html#sec_imports-as-views-on-exports

numblock
  • 63
  • 5

1 Answers1

3

In JavaScript primitive types are passed by value.

let counter = 1;

let factory = () => {

  ...

  // The property `counter` in this object gets passed a value of the `counter` declared in the scope of `factory.js`.
  // It does not get a reference.
  return { counter, increment };
}

When you return the object from the factory function, its property counter is assigned a value from the counter declared in the scope of factory.js. This essentially means the counter property on the object received a copy of the value – there is nothing linking the value of the counter variable and the counter property.

let counter = 1;

let factory = () => {
  let increment = function () {
    // The `++` operates on the `counter` variable declared in the scope of `factory.js`.
    // There is no link between the value `counter` in the scope of `factory.js` and the `counter` as the property of the object returned.
    // As a result when the `counter` variable is incremented, the `counter` property remains the same as the value initially passed to it.
    counter++;
  };
};

When you increment the counter, you are incrementing the value of the variable declared in the scope of factory.js. The variable's value is a Number therefore being a primitive. Primitive values are passed by value so there is no link between the variable counter and the property counter.

Hopefully all that makes sense. If you want to do some more reading on this idea of passing by value (compared to passing by reference) you can see the following StackOverflow questions:

At this point in time you might be asking how can I fix this?

Instead of incrementing counter you need to increment this.counter. Like this:

let increment = () => {
  this.counter++;
};

This works because the context of this is the object being returned from the function factory. The counter property is on the variable you assigned the result of calling factory.

Community
  • 1
  • 1
Wing
  • 8,438
  • 4
  • 37
  • 46
  • 1
    thanks @wing, for you detailed answer! it made me realise that it was the new destructuring syntax that was confusing me in: `return { counter, increment };` i was getting the property and the value mixed up. – numblock Nov 20 '16 at 13:13
  • _"In JavaScript primitive types are passed by value."_ Everything is passed by value, but that's not relevant, because in the OP's example nothing is passed at all. _"the context of this is the function factory"_ The context is irrelevant and the value of `this` is the object returned by the factory. – a better oliver Nov 21 '16 at 09:31
  • @numblock _"it was the new destructuring syntax that was confusing me"_ That's not destructuring, it's an object literal. – a better oliver Nov 21 '16 at 09:36
  • @numblock no worries. Just to add to the comment from zeroflagl: what you are using there is not [destructuring](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) but **[shorthand property assignment](http://es6-features.org/#PropertyShorthand)**. – Wing Nov 21 '16 at 11:09
  • @zeroflagL "_Everything is passed by value, but that's not relevant, because in the OP's example nothing is passed at all._" In the OP's example the value of the `counter` variable is passed to the object being returned. When I first read the question I interpreted it as the OP intending to increment a value on an object property which they believed was linked to the original declaration of a variable of the same name. Therefore I explained the cause of the issue – a misunderstanding on how the value of `counter` is passed to the object being returned and how to access it. – Wing Nov 21 '16 at 11:17
  • @zeroflagL "_The context is irrelevant_" The context is relevant because the OP is trying to access a data store called `counter`. This store sits in two contexts, with one being the object being returned. The OP was trying to access the `counter` property in that context. – Wing Nov 21 '16 at 11:22
  • @zeroflagL "_the value of `this` is the object returned by the factory._" yes that's correct. Thanks for pointing that out, I'll correct my answer. – Wing Nov 21 '16 at 11:23
  • @zeroflagL "_Everything is passed by value_" that's not technically true, see the SO question I linked in the answer: [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) – Wing Nov 21 '16 at 11:29
  • In the OP's example the value is assigned, not passed. And it's true: everything is passed by value. Instead of referring to an inaccurate answer you could refer to the specification. Or the correct answer (There is one). _"The context is irrelevant"_ may be a bit misleading. What it means here is that the **value** of `this` is the only thing that matters. – a better oliver Nov 21 '16 at 11:47