0

Lets say I have two APIs and I would like to inherit classes from the first, but modify the response with .prototype.toJSON().

When I inherit from the first class, how can I inherit class methods as well.

An example

//file v1/models/users.js
var UserModel = function() {
  this.id = 0;
  this.firstName = '';
  this.lastName = '';
}

UserModel.find = function(q, callback) {
  //find any user that matches q and map them to UserModel
  if (err) return callback(err, null);
  callback(null, users);
}

module.exports = UserModel;

And the the next version

//file v2/models/users.js
var UserModel = require('../v1/models/users');

function UserModelV2() {
  UserModel.call(this);
}

UserModelV2 = Object.create(UserModel.prototype);
UserModelV2.prototype.constructor = UserModel;

UserModelV2.prototype.toJSON = function() {
    var obj = {};
    obj.firstName = 'foo';
    return obj;
}
module.exports = UserModelV2;

When I now try to call

var User = require('./v2/models/users');
User.find(1);

I get an error saying User.find does not exists.

I am aware I am only inheriting prototypal properties, but I cannot find an example of inheriting class methods anywhere.

5
  • User.prototype.find - a function's .prototype determines the prototype (__proto__ property) of objects created by using that function as a constructor. Commented Jul 8, 2017 at 22:38
  • Just seen this, I think you mean UserModelV2.prototype = Object.create(UserModel.prototype); Commented Jul 8, 2017 at 23:57
  • @Thomas No. that is the correct use of Object.create(). Commented Jul 9, 2017 at 0:19
  • @ScottMarcus usually the constructor of a class is a function, not an instance of another class. Currently he's overwriting/replacing the constructor of UserModelV2 with an instance of UserModel. And while you can still instantiate this construct with Object.create() you can not use the new keyword anymore. I'm not sure, that this is what he intended. Commented Jul 9, 2017 at 0:29
  • @Thomas I know. See my answer for the proper usage and note that I've removed the Object.create completely because the OP is using both techniques and doesn't need to. Commented Jul 9, 2017 at 0:46

2 Answers 2

3

Don't add find directly onto UserModel because that causes the method to be added only to one instance.

Add it to the prototype:

UserModel.prototype.find = function(id) {
  //find the user by id and return 
}

Because all instances of UserModel will inherit from the prototype of your constructor function.

Then, your next version would inherit from the first like this:

// Constructor of sub-class
function UserModelV2() {
  // Call the prototype.constructor, not just .constructor
  UserModel.prototype.constructor.call(this);
}

// Perform inheritance
UserModelV2.prototype = new UserModel();

// Correct the constructor of the prototype
UserModelV2.prototype.constructor = UserModelV2;

// Extend the sub-class
UserModelV2.prototype.toJSON = function() {
    var obj = {};
    obj.firstName = 'foo';
    return obj;
}

By the way (and this could be why you got stuck on this), technically (and despite the class keyword), JavaScript does not have classes, it has prototypes and they are the basis for inheritance.

Sign up to request clarification or add additional context in comments.

8 Comments

But then I would need to do var user = new UserModel(); user.find(id); every time. Or is there another way?
When you create a function to serve as your object basis, then you are making a constructor function and yes, that is how constructor functions are meant to be used. Your function indicates that you want individual instances because you are using this, which is exactly how you make instance properties. You can't have instance properties without constructing an instance (i.e. new).
Any other way to do what I would like to do? Maybe my example wasn't the best, because find can actually return more instances of User. I'll update my question.
@Ron Well, you have to decide whether you want/need instance properties or not (i.e. this.id = 0;). because if you have instance properties, you need to make individual instances.
Yes, I want instance properties, but I would like to share certain functions to update, delete, add and find users, but only the toJSON response needs to be different in V2.
|
0

Well, you can simply copy the methods over from UserModel

Object.assign(UserModelV2, UserModel);

Or you define that the UserModelV2 function inherits its properties from the UserModel function instead of Function.prototype directly.

Object.setPrototypeOf(UserModelV2, UserModel);

Or you use the new class syntax and let that take care of this:

class UserModelV2 extends UserModel {
  toJSON(){
    return {firstName: 'foo'};    
  }
}

maybe in combination with a transpiler like babel, if you need backwards compatibility.

I'd encourage the last option. To me this seems like the cleanest approach and provides you with the ability to not use the transpiler.
And it is semantically clear. Even the JS compiler can tell that you're not just arbitrarily messing around with the prototype of some function and may break something or do something "weird".

2 Comments

When using assign it finds the 'find' function, but also the toJSON of the old property is used.
there is no "old" toJSON method on your UserModel. And you have to assign the old properties before you attempt to overwrite them with new ones 😉

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.