Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can javascript constructor function and object.create be combined?

Update

If this is not possible, please feel free to provide an answer explaining why. I'd be happy to mark as it accepted.


I'd like to slightly simplify the following code (two steps for an object "declaration", I'd like to have one):

var Masher = function(opts) {
    this._name  = opts.name;
};

Masher.prototype = Object.create(Object.prototype, {
    _name:  { writable: true },
    name:  { get: function() { return this._name;  }}
});

// Note: (new Masher({name: 'bar'})).name == 'bar'

I would to create the entire function prototype in one shot with the constructor function appearing somewhere in the Object.create. Perhaps, something like this:

var Basher = Object.create(Function.prototype, {
    _name:  { writable: true },
    name:  { get: function() { return this._name;  }},
    constructor: { value: function(opts) { this._name = opts.name; }}
});

However, when I call new Basher(), I get: 'TypeError: object is not a function'.

Although I realize I could do this with syntactic sugar (a helper library), my goals here are to keep things as simple as possible and pick up some understanding of the JS object, prototype, constructor internals. I've tried to read as much on this as possible: SO related questions, Crockford, Ben Nadel, Joost Diepenmaat.

Perhaps I haven't found the correct formulation, or I'm fighting the design philosophy of Object.create, or the language doesn't allow this. Perhaps, this is really just a stylistic thing, and therefore, a conceit.

Of course, I can live with the two step process (Masher). There's something about packaging it all in one shot that feels right (Basher).

Is there a way to do this? Thanks.

like image 838
Andrew Philips Avatar asked Sep 30 '22 17:09

Andrew Philips


1 Answers

If you want to use the class-based approach where you can call a constructor function with new, you will always have two parts:

  • The constructor function itself (to initialise instances)
  • The prototype object (for shared properties)

If you don't want to drop the prototype entirely, there is no JavaScript syntax to do both the function creation and the prototype setup in one go (apart from the new ES6 class syntax, of course) while still maintaining the .prototype link from the function to the prototype object. Of course, a trivial helper function (doesn't need to be a complete library) would do:

function Class(p) {
    return (p.constructor.prototype = p).constructor;
}
var Casher = Class({
    constructor: function(opt) { this._name = opt.name },
    get name() { return this._name }
});
var foo = new Casher({name:'bar'});

This patterns doesn't really have a lot to do with Object.create at all (except you want your prototype inherit from another one).

So yes, maybe you are trying to fight the philosophy of Object.create, which is to use only objects and derive other objects from these (read the Wikipedia article on it and make sure to check out some example from languages other than JS). You would not have a constructor, and not a new operator - rather you'd call a create method on your object:

var Proto = { // some helper methods (usually native in more prototype-focused languages)
    clone: function() {
        return Object.create(this);
    },
    create: function(opt) {
        var derived = this.clone();
        derived.init(opt);
        return derived;
    },
    init: function(opt) {
        Object.getOwnPropertyNames(opt).forEach(function(p) {
            Object.defineProperty(this, p, Object.getOwnPropertyDescriptor(opt, p));
        }, this);
    }
};

var Pasher = Proto.create({ // "subclass" Proto
    init: function(opt) {
        if ("name" in opt) this._name = opt.name;
    },
    _name: "",
    get name() { return this._name; }
});
var foo = Pasher.create({name:'bar'});
like image 80
Bergi Avatar answered Oct 03 '22 00:10

Bergi