Reputation: 53
I encountered this issue when creating a function for a Javascript library. As far as I can tell, when an object's function uses a combination of a closure and a promise, the most recently created version would be used for all instances. The output of the below code is "3" repeated three times. However, when I add var
in front of the line defining logSecret
, the output is then 1 2 3, which is what I originally expected.
Why do multiple instances of the object end up using the same value?
Why does adding var
in front of the closure fix the issue?
Unfortunately I don't know Javascript quite well enough to understand what exactly is happening here, so I'm hoping someone can shed some light on what is happening here. Thank you!
function MyObject(myValue){
var _this = this;
_this.secret = myValue;
_this.foo = function() {
makePromise = function() {
var promise = $.Deferred();
promise.resolve("done");
return promise;
}
logSecret = function() {
console.log(_this.secret);
}
var promise = makePromise();
$.when(promise).then(function (data) { logSecret(); });
}
}
var obj1 = new MyObject(1);
var obj2 = new MyObject(2);
var obj3 = new MyObject(3);
obj1.foo();
obj2.foo();
obj3.foo();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Upvotes: 0
Views: 805
Reputation: 55729
In non-strict mode, when variables are not explicitly declared with var
, let
or const
, an implicit global variable is created. ie. such variables behave as if they had been declared in the global context.
In your code, the variables makePromise
and logSecret
are not explicitly declared and so are shared by every invocation of MyObject
.
So, every time MyObject
is invoked, the makePromise
and logSecret
are overwritten with a new function object instance that closes over the lexical environment for the latest invocation of MyObject
.
The final invocation of MyObject
closes over the secret value of 3
, hence the observed behavior.
function MyObject(myValue) {
var _this = this;
_this.secret = myValue;
_this.foo = function() {
makePromise = function() { // global variable
var promise = $.Deferred();
promise.resolve("done");
return promise;
}
logSecret = function() { // global variable
console.log(_this.secret);
}
var promise = makePromise();
$.when(promise).then(function(data) {
logSecret();
});
}
}
Upvotes: 2