0

I have a function, which I have prepared for a constructor call...

function Queue() {
  if (!(this instanceof Queue)) return new Queue();
    this.capacity = {};
    this._count = 0;
}

And these method are being set on the the prototype property of Queue...Everything kosher right?

  Queue.prototype.enqueue = function(name, options) {
    this.capacity[name] = options || {};
    this.count();
    if (this._count > 5) {
      return 'Max capacity has been reached—which is five, please dequeue....'
    }
  };

  Queue.prototype.count = function() {
    var total = Object.keys(this.capacity);
      total.forEach(function(elem) {
        this._count++
      });
      if (this._count == 1) {
        console.log(this.capacity[Object.keys(this.capacity)])
      console.log( 'There is one item in the queue');
      } else {
        console.log(this.capacity[Object.keys(this.capacity)])
        console.log( 'There are ' + this._count + ' items in the queue');
      }
   };

My question how do i get this._count to increment when the enqueue/count method fires? I keep getting:

There are 0 items in the queue

I know I could add it on the .prototype property and place that in the count function and have it reference a local var...

Queue.prototype.count = function() {
    var total = Object.keys(this.capacity), count = 0;
    total.forEach(function(elem) {
      this.count++
    });

Queue.prototype.call = call // <-- weird no?

if (this.count == 1) {
    console.log(this.capacity[Object.keys(this.capacity)])
  console.log( 'There is one item in the queue');
} else {
     console.log(this.capacity[Object.keys(this.capacity)])
  console.log( 'There are ' + this.count + ' items in the queue');
}

};

But that seems not to be elegant...

Thanks in advance!

1
  • You could simplify to this._count += Object.keys(this.capacity).length. And didn't you mean to reset _count to zero or use = instead of +=? Commented May 19, 2017 at 22:45

3 Answers 3

2

You need to bind this within forEach

Queue.prototype.count = function() {
    var total = Object.keys(this.capacity);
      total.forEach(function(elem) {
        this._count++
      }.bind(this)); //bind the context
      if (this._count == 1) {
        console.log(this.capacity[Object.keys(this.capacity)])
      console.log( 'There is one item in the queue');
      } else {
        console.log(this.capacity[Object.keys(this.capacity)])
        console.log( 'There are ' + this._count + ' items in the queue');
      }
   };
Sign up to request clarification or add additional context in comments.

2 Comments

or use an arrow function with lexical this binding or simply do the outdated var self = this; and use self within the callback.
Agreed. There are lot of solutions. But my guess is the user wants the solution to work in IE10 , IE 11. Other wise he would have used ES6 syntax everywhere.
1

Try following modification (bind the function):

total.forEach(function(elem) {
   this._count++
}.bind(this));

The problems is that this refers to a different object than in the parent function, because in JS, closures do not preserve this but instead the caller decides the this value. Alternatively, you can use the second thisArg argument of foreach.

Comments

1

The existing answers provide good solutions to the problem itself, I just thought I'd elaborate a bit more on the why.

this is a reference assigned by the execution context. More plainly it's a reference that's determined by the call site of the function. Since you can pass functions around in JavaScript like any other value this can lead to problems being caused by that reference being a moving target.

The issue with your code is that you're referring to this inside of a forEach. forEach takes a function as an argument and calls it, since what this is pointing to is determined by where the function is called and not where it's defined the value is something different when it gets called. It ends up falling back to whatever global context or undefined if you're in strict mode.

There are a number of different ways to handle the problem.

You could store a reference to the outer this on a variable and use it inside the other function.

var self = this;
total.forEach(function(elem) {
  self._count++;
});

You could use .bind. It's a function method which returns a function that uses the passed in object as the reference for this no matter where you call it.

total.forEach(function(elem) {
  this._count++;
}.bind(this));

Or you could use an arrow function. Arrow functions don't create their own context so they'll just maintain the value of this from the surrounding one.

total.forEach(() => {
  this._count++;
});

This is a common problem and these are all valid solutions. They go from least to most elegant in my opinion.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.