JSilv
JSilv

Reputation: 1055

Set prototype of constructor function without using Object.setPrototypeOf?

I have been working on an ORM-like method of handling my models in a Node.JS app, and I am satisfied with how it works; however, I am wondering if there is a better way to handle resetting the prototype of a constructor function.

The behavior I want is something like this:

function Person(data) {
  for (const key in data) {
    this[key] = data[key]
  }
}

// setting methods and so on here

Person.findAll() // returns a list of all persons from the db
Person.findOne(id) // returns a person at that ID

const self = new Person(data) // creating a person object
self.save() // saves that object in the db
self.update(data) // updates person object
self.addFriend(id) // creates a record adding the given id as a friend

There are methods on the constructor Person that have access to all Person records, while each instance of Person has methods that affect just that one record. I'm managing this like so:

const utils = (tablename) => ({
  findAll: () => dbHandler(`SELECT * FROM ${tablename}`),
  findOne: id => dbHandler(`SELECT * FROM ${tablename} WHERE id = $1`, [id]),
  // etc
})

Object.setPrototypeOf(Person, utils('people'));

.setPrototypeOf isn't the best method to use; it's not particularly performant. I'm wondering if there is a better way to handle a use case like this -- for example, is there anything in the es6 class spec that would handle this? (Extending classes isn't quite what I'm looking for; the distinction between Person.findAll() and self.update(data) is important to me, and I would like the util methods to be on the constructor itself.)

Edit: Shoutout to Bergi below for the solution ("just use Object.assign")... here's a little demo:

const dbHandler = (string, arr) => console.log(string, arr)

function Person(data) {
  for (const key in data) {
    this[key] = data[key]
  }
}

const utils = (tablename) => ({
  findAll: () => dbHandler(`SELECT * FROM ${tablename}`),
  findOne: id => dbHandler(`SELECT * FROM ${tablename} WHERE id = $1`, [id]),
  // etc
})

Object.assign(Person, utils('people'));

Person.prototype.save = function() {
  console.log(`Saving person with data ${JSON.stringify(this)} to a totally real database`)
}

Person.findAll()
Person.findOne(1)

const self = new Person({
  name: 'j',
  class: 'wizard'
})
self.save()

Upvotes: 2

Views: 83

Answers (1)

Bergi
Bergi

Reputation: 664513

There's no reason to involve any inheritance here (and setting the prototype of a constructor function so that it no longer inherits from Function.prototype is really weird). Just assign the static methods as properties of the Person constructor object:

Object.assign(Person, utils('people'));

This has the same effect as

function createUtils(obj, tablename) {
  obj.findAll = () => dbHandler(`SELECT * FROM ${tablename}`);
  obj.findOne = id => dbHandler(`SELECT * FROM ${tablename} WHERE id = $1`, [id]);
  // etc
}
createUtils(Person, 'people');

Upvotes: 2

Related Questions