Reputation: 7759
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!
Upvotes: 0
Views: 50
Reputation: 849
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.
Upvotes: 1
Reputation: 990
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.
Upvotes: 1
Reputation: 12176
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');
}
};
Upvotes: 2