Sub methods in Javascript classes

Viewed 316

The problem

I'd like to be able to use classes to create client objects and add address objects to an array within them.

What I'd like to be able to do

let client = new Client ('A123', 'John', 'Smith', avatarUrl);
client.address('home').add(address);

My current classes

Working version

class Client extends Name {
  constructor(clientRef, firstName, lastName, profilePic) {
    super (firstName, lastName, profilePic);
    this._id = clientRef;
    this.addresses = [];
  }
  addAddress (address, label = 'main') {
    address = {...address, label}
    this.addresses.push(address);
  }
  removeAddress (label = 'main') {
    this.addresses = this.addresses.filter((element) => { return element.label != label; });
  }
}

Non-working version

This is what I'm trying to create, but it doesn't work.

class Client extends Name {
  constructor(clientRef, firstName, lastName, profilePic) {
    super (firstName, lastName, profilePic);
    this._id = clientRef;
    this.addresses = [];
  }
  address (label = 'main') {
    add (address) {
      address = {...address, label}
      this.addresses.push(address);
    }
    remove () {
      this.addresses = this.addresses.filter((element) => { return element.label != label; });
    }
  }
}
3 Answers

You're trying to assign values to a class method as if it were a key-value object store. It's a function that will return a null reference without a return statement. What you need to do it return a key-value store that has the methods you want, like this:

class Client extends Name {
  constructor(clientRef, firstName, lastName, profilePic) {
    super (firstName, lastName, profilePic);
    this._id = clientRef;
    this.addresses = [];
  }
  address (label = 'main') {
    return {
      add: (address) => {
        address = {...address, label}
        this.addresses.push(address);
      },
      remove: () => {
        this.addresses = this.addresses.filter((element) => { return element.label != label; });
      }
    }
  }
}

What you can do is to return an object holding these functions, but you need to ensure that this points to the original Client object, and for that, you could utilize arrow functions:

class Client extends Name {
  constructor(clientRef, firstName, lastName, profilePic) {
    super(firstName, lastName, profilePic);
    this._id = clientRef;
    this.addresses = [];
  }
  address(label = 'main') {
    return {
      add: (address) => {
        address = {
          ...address,
          label
        }
        this.addresses.push(address);
      },
      return: () => {
        this.addresses = this.addresses.filter((element) => {
          return element.label != label;
        });
      }
    }
  }
}

You can do something like this to make it work:

address (label = 'main') {
  return {
    add: (address) => {
      this.addresses.push({...address, label});
    },
    remove: () => {
      this.addresses = this.addresses.filter((element) => { return element.label != label; });
    }
  }
}

But I recommend to move address to another class and return it as result.

class Address {

  constructor(label) {
    this.label = label;
  }

  add(address) {
    for (const name in address) {
      this[name] = address[name];
    }
  }
}

class Client {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this.addresses = [];
  }

  address(label) {
    const address = new Address(label);

    this.addresses.push(address);

    return address;
  }
}


const client = new Client('John', 18);

client.address('home').add({
  str: 'Main',
  house: 55
})
Related