I'm trying to use JS with classic prototypical inheritance, instead of the new ES6 class model, mainly to be able to access the closure scope.
In the example bellow, I want to expose a variable current
declared inside the function Counter
trough this
object created by the new
operator.
function Counter(start, stop) {
var current = start;
function inc() { if (current < stop) return current++ }
function getCurrent() { return current }
Object.assign(this, { inc, getCurrent,
get current() { return current }, set current(value) { current = value }
})
}
counter = new Counter(0, 3)
while ((v = counter.inc()) !== undefined)
console.log(counter.getCurrent(), counter.current)
I expected the following output:
1 1
2 2
3 3
Because counter.current
& counter.getCurrent()
should both return the same result. But instead, I'm receiving
1 0
2 0
3 0
If I replace that Object.assign(...)
with the code bellow, it works as expected.
Object.assign(inc, getCurrent })
Object.defineProperty(Counter.prototype, 'current',
{ get: () => { return current }, set: (value) => { current = value }
I could use this model, (and currently using), but I would like to use the former, because is simpler and less verbose. It seems that there are 2 different scopes here.
I tested with node 10, Chrome 73 & Firefox 68, and received the same results.
What I'm missing here?
In the example above, I tried to be as terser as I could. But to be more precise and better illustrate the point, follow a more complete test, with some commented things I've tried.
Here, I renamed the variable current
to _current
in order to avoid confusion with the current
property, but that shouldn't be obligatory.
function Counter(start, stop) {
var _current = start;
function inc() { if (_current < stop) return _current++ }
function getCurrent() { return _current }
// Object.assign(this.constructor.prototype,
// Object.assign(this.__proto__,
// Object.assign(Counter.prototype, {
Object.assign(this, {inc, getCurrent,
get current() { return _current }, set current(value) { _current = value }
// get current() { return current } // supposed to be read-only, but not
})
// This works as expected
// Object.defineProperty(Counter.prototype, 'current',
// { get: () => { return _current }, set: (value) => { _current = value } })
}
counter = new Counter(0, 3)
while ((v = counter.inc()) !== undefined) {
console.log(counter.getCurrent(), counter.current)
counter.current -= 0.5
}
the output of the code above is:
1 0
2 -0.5
3 -1
Where that counter.current -= 0.5
is storing its value?
When your code calls Object.assign()
, it's passing in an object that has a getter and setter for current
. Thus the Object.assign()
process will itself invoke that getter to get the value for the property "current" while it's copying the property values to the new object. Thus, the Counter object ends up without the getter and setter, which explains your results. Its "counter" property is just a simple property with a copy of the value of the local counter
variable at the time the constructor code ran.
Object.assign()
just copies property values, accessing them the same way any other code would.
Note that if you don't call Object.assign()
at all, and just return the object you're passing into it, you'll get a working object that behaves like you expect.