Jason Black
Jason Black

Reputation: 245

Node.js: can't call one Javascript prototype method from another?

I'm trying to define a class with two public methods. It so happens that one of the methods is useful for implementing the other one, but for the life of me, I cannot get that function to be recognized within the scope of the other one. I have no idea whether this is something specific to node.js or not, but in case it matters, this is running under node.

Here is a short toy problem which reproduces the behavior I'm seeing:

function Foo() {
    this.list = [];
}

Foo.prototype = {
    addSeveral: function(arr) { arr.forEach(function(v) { addOne(v)} ) },
    addOne: function(item) { list.push(item); }
}

f = new Foo();
f.addSeveral([1,2,3]);

When I run this, it blows up with:

[jasonb@localhost]$ node testobj.js

/home/jasonb/work/testobj.js:6
Foo.prototype.addSeveral = function(arr) { arr.forEach(function(v) { addOne(v)}
                                                                     ^
ReferenceError: addOne is not defined
    at /home/jasonb/work/entity_mapping/testobj.js:6:70

I've tried every variation I can find on how to assign stuff to a class's prototype, with the same results. And if I try to call this.addOne(v) instead, it still blows up, but with TypeError: Object #<Object> has no method 'addOne'

How can I call addOne() within addSeveral()?

Upvotes: 3

Views: 3795

Answers (2)

Jonathan Ong
Jonathan Ong

Reputation: 20315

You can pass this to forEach:

addSeveral: function (arr) {
  arr.forEach(function (v) {
    this.addOne(v)
  }, this)
}

Or you can use bind:

addSeveral: function (arr) {
  arr.forEach((function (v) {
    this.addOne(v)
  }).bind(this))
}

But what I would do is push.apply:

addSeveral: function (arr) {
  [].push.apply(this.list, arr)
}

Upvotes: 1

josh3736
josh3736

Reputation: 144872

Because forEach invokes a callback function for each element of the array, you're losing context (this is not the Foo object inside your callback). You can close over a reference:

Foo.prototype = {
    addSeveral: function(arr) { var self=this; arr.forEach(function(v) { self.addOne(v)} ) },
    addOne: function(item) { this.list.push(item); }
}

...or just use a regular loop, which will have much better performance – function invocation is expensive.

addSeveral: function(arr) { for(var i = 0; i < arr.length; i++) this.addOne(arr[i]); };

Upvotes: 9

Related Questions