Odd822
Odd822

Reputation: 53

Multiple instances of objects using the same function as the newest instance

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

Answers (1)

Ben Aston
Ben Aston

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

Related Questions