0

I have a Person constructor and I want to add a method supposed to add friends. I want to allow my user to pass a variable number of friends so I thought about the new "rest" feature of ES6. Sadly, I can't find my way out. Here's my first try (error : "Uncaught TypeError: f.addFriends is not a function(…)"):

// Persons creator
function Person(name){
    this.name = name;
    this.friends = [];
    this.addFriends = function(...a){
      a.forEach(function(d){this.friends.push(d)});
    }
}

// Create three persons
f = new Person("Fanny");
e = new Person("Eric");
j = new Person("John");

// add Eric & Fanny as friends of Fanny
f.addFriends(e,j);

I've also tried the following code (no error, but no friends added):

// Persons creator
function Person(name){
    this.name = name;
    this.friends = [];
}

Person.prototype.addFriends = function(...a){
   a.forEach(function(d){this.friends.push(d)});
}


// Create three persons
f = new Person("Fanny");
e = new Person("Eric");
j = new Person("John");

// add Eric & Fanny as friends of Fanny
f.addFriends(e,j);

What am I doing wrong? Many thanks for your help!

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
Le Sparte
  • 163
  • 2
  • 12

4 Answers4

3

forEach takes a callback, which is usually called in the global context (window in the browser). You would need to either pass the current this into forEach as the second argument.

Or could avoid the whole this problem altogether and just concat the Arrays:

function Person(name){
    this.name = name;
    this.friends = [];
    this.addFriends = function(...a){
      this.friends = this.friends.concat(a);
    }
}
nils
  • 25,734
  • 5
  • 70
  • 79
  • There's a much better solution than using concat, look at my answer. BTW your code has the side effect of replacing the origina array instead of augmenting it. – Denys Séguret Apr 17 '16 at 17:30
  • Unfortunately, your solution creates nested arrays instead of a single array. And yes, this does replace the current array with a new array. Most of the time, this shouldn't be a problem though. See Bergi's comment to fix your code. – nils Apr 17 '16 at 17:34
1

this, in the callback passed to forEach, isn't your instance of Person in this code:

Person.prototype.addFriends = function(...a){
   a.forEach(function(d){this.friends.push(d)});
}

You could use the new arrow function to have the right context:

Person.prototype.addFriends = function(...a){
   a.forEach((d) => {this.friends.push(d)});
}

but there's a more elegant solution here:

Person.prototype.addFriends = function(...a){
   this.friends.push(...a);
}
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
0

Since you use a callback inside the forEach, the this doesn't refer to the object. Bind the callback to this:

Person.prototype.addFriends = function(...a){
   a.forEach(function(d){this.friends.push(d)}.bind(this));
}

Since we're using ES6, you can use an arrow function instead. Arrow functions lexically bind the this value:

Person.prototype.addFriends = function(...a){
   a.forEach((d) => this.friends.push(d));
}
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
0

You can use a new feature from ECMAScript 6 -> classes

  1. define your class:

    class Person {

    constructor(name) {
        this.name = name;
        this.friends = [];
    }
    
    addFriends(friends) {
        // do someting with friends
        this.friends = friends
    }
    

    }

then you are able to create new instance of the Person

var p = new Person("Jack");

and add some new friends

p.addFriends(....)