Can javascript constructor function and object.create be combined? - Stack Overflow

UpdateIf this is not possible, please feel free to provide an answer explaining why.I'd be happy

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.

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.

Share Improve this question edited May 23, 2017 at 12:34 CommunityBot 11 silver badge asked Oct 6, 2014 at 14:32 Andrew PhilipsAndrew Philips 2,18920 silver badges26 bronze badges 1
  • 1 No, you cannot. You need to create two separate objects: the constructor function and the prototype object. – Bergi Commented Oct 6, 2014 at 14:45
Add a ment  | 

3 Answers 3

Reset to default 4

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 plete 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'});

Object.create() returns an object, not a function, with a specific prototypal inheritance chain defined.

var Basher = Object.create(Function.prototype, {
    _name:  { writable: true },
    name:  { get: function() { return this._name;  }},
    constructor: { value: function(opts) { this._name = opts.name; }}
});
> undefined
Basher
> Object {_name: undefined, name: (...)}_name: undefinedconstructor: function (opts) { this._name = opts.name; }name: (...)get name: function () { return this._name;  }__proto__: function Empty() {}
> typeof Basher
"object"

You can, however, bine Object.create() and constructor functions to let you reuse object literals as APIs, which makes the code look a bit cleaner:

var basherAPI = {
    name: function () {
        return this._name;
    }
};

function Basher(name) {
    var inst = Object.create(basherAPI);
    // assign instance *state* here; the API is
    // reusable but each object needs its own state
    inst._name = name;
    return inst;
}

var basher = new Basher('Tom');
basher.name() // Tom

EDIT: In this case, using the new keyword is purely convention; it has NO bearing on what happens within the constructor function. One could also write: var basher = Basher('Tom');.

I'm putting an answer in here, but I'm not sure this fully answers my question, so I won't be marking it as the answer as I don't believe I pletely understand this part of the JS language.

The closest I believe I can get to this is the following:

var Lasher;

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

This formulation exposes part of what is going on, something to which @Bergi alluded, albeit briefly. First, note that it does not answer my question as it still requires two steps (Variable declaration and then Value assignment). It does get to the heart of my stylistic issue, which is to co-locate all of the object's declaration code. It does this by capturing the anonymous function definition (constructor) in Lasher, then referencing the prototype property in order to assign it the prototype object (the rest of the "class" declaration).

Although grouped together as one putational unit, visually it's harder to parse and fairly ugly, quite frankly. Also, the anonymous function might even capture Lasher in its scope (I'm not sure).

Incidentally, I tried this flawed approach. It's one statement, although still a bit ugly:

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

Too bad it doesn't work. Crasher is scoped to the first () and doesn't live in the outer scope, which is why the earlier code had to declare Crasher as a variable in order to capture the anonymous function.

The thing is, Lasher is not really simpler than Masher (above) from a line count or readability perspective. So, the original formulation stands as the best (so far).

Also, although I understand why Object.create cannot be used as it creates an Object, I guess I don't understand how the internal JS interpreter creates functions vs. Objects. What I mean is, a Function is an Object, but I guess there's no way to create a function that can be returned directly from Object.create. That's the primary problem.

In addition, whatever is going on inside of the prototype assignment and the constructor assignment within that, adding a constructor property in the Object.create doesn't work. We can see that with this bit of code:

var Masher = function(){ console.log("default");};

Masher.prototype = Object.create(Object.prototype, {
    _name:  { writable: true },
    name:  { get: function() { return this._name;  }},
    constructor: { value: function(opts) {
        console.log("ctor");
        this._name  = opts.name;
    return this;
    }}
});
// "new Masher()" outputs:
// default
// undefined

So, if I understand some of this correctly, function creation and prototype creation on top of that have some deep relationships within the JS interpreter/piler and the language does not provide a means to do what I am trying to do.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745413854a4626656.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信