Correct javascript inheritance

后端 未结 2 1953
灰色年华
灰色年华 2020-11-22 05:54

I\'ve been reading a lot of articles about \"inheritance\" in javascript. Some of them uses new while others recommends Object.Create. The more I r

相关标签:
2条回答
  • 2020-11-22 06:04

    Simple: Object.create is not supported in all environments, but can be shimmed with new. Apart from that, the two have different aims: Object.create just creates a Object inheriting from some other, while new also invokes a constructor function. Use what is appropriate.

    In your case you seem to want that RestModel.prototype inherits from Model.prototype. Object.create (or its shim) is the correct way then, because you do not want to a) create a new instance (instantiate a new Model) and b) don't want to call the Model constructor:

    RestModel.prototype = Object.create(Model.prototype);
    

    If you want to call the Model constructor on RestModels, that has nothing to do with prototypes. Use call() or apply() for that:

    function RestModel() {
        Model.call(this); // apply Model's constructor on the new object
        ...
    }
    
    0 讨论(0)
  • 2020-11-22 06:22

    JavaScript is an OOP language based on prototypes and not classes. That is one of the cause of all this confusion you can see around: a lot of developers use JS as it was a class-based.

    Therefore, you can see a lot of libraries that trying to use paradigm of a class-based language in JS.

    In addition, JS itself is lacking of some traits that makes inheritance, also prototype-based, painful. For example, before Object.create there wasn't a way to create an object from another object (as a OOP prototype-based language should do), but only using a function as constructor.

    And also for that reason, you can see a lot of libraries that are trying to "fix" the language, every one in its own way.

    So, your confusion is normal. Say that, the "official" way is use prototype property, functions as constructor, and Object.create to create from object's insteance. However, as said above, in some cases the code is verbose or it lacks of functionality, so this procedure is often wrapped somehow, and then it becomes a matter of personal taste.

    Because you're talking about model, you can take a look to Backbone's Model and see if this approach fits you.

    Update: to give some example that I hope helps you to clarify, here the old school inheritance way using new:

    function Model() {
        this.foo = "foo";
        this.array = [];
    }
    
    Model.prototype.foo = "";
    Model.prototype.array = null;
    Model.prototype.doNothing = function() {};
    
    function RestModel() {
        this.bar = "bar";
    }
    
    RestModel.prototype = new Model;
    RestModel.prototype.bar = ""
    
    var myModel = new RestModel();
    

    Now, here the issues:

    1. The constructor of Model is called once, only when the RestModel.prototype is set. Therefore, the foo property for every RestModel instance will be "foo".
    2. Not only that, but all RestModel instances will share the same instance of the same array in this.array property. If you create an instance of Model directly, you have a new instance of array for each instances.
    3. myModel.constructor is Model instead of RestModel. That's because we override prototype with a new instance of Model, that contains constructor equals to Model.
    4. If you want to have a new instance of RestModel with a lazy call to the constructor, is not possible.

    I think there are the main points, of course they're not the only ones. So, how to solve that issues? As I said, a lot of people did already that, and they create library and frameworks also to limit the verbosity. However, to have an idea:

    function Model() {
        this.foo = "foo";
        this.array = [];
    }
    
    Model.prototype.foo = "";
    Model.prototype.array = null;
    Model.prototype.doNothing = function() {};
    
    function RestModel() {
        Model.call(this);
    
        this.bar = "bar";
    }
    
    RestModel.prototype = Object.create(Model.prototype);
    RestModel.prototype.constructor = RestModel;
    RestModel.prototype.bar = ""
    
    var myModel = new RestModel();
    
    // for lazy constructor call
    var anotherModel = Object.create(RestModel.prototype);
    
    RestModel.call(anotherModel);
    

    Notice that if you don't want to use prototype or function as constructor, with Object.create you can do that:

    var Model = {
        foo: "",
        array: null,
        doNothing: function() {}
    }
    
    var RestModel = Object.create(Model);
    
    RestModel.bar = "";
    
    var myModel = Object.create(RestModel);
    
    // Assuming you have to set some default values
    initialize(RestModel);
    

    Or:

    var Model = {
        foo: "",
        array: null,
        doNothing: function() {},
    
        init : function () {
            this.foo = "foo";
            this.array = [];
        },
    }
    
    var RestModel = Object.create(Model);
    
    RestModel.bar = "";
    RestModel.init = function () {
        Model.init.call(this);
    
        this.bar = "bar";
    }
    
    var myModel = Object.create(RestModel);
    
    myModel.init();
    

    In that case you mimic basically the constructor. You can also pass a descriptor to Object.create (see the docs) but it becomes more verbose. Most of the people use a sort of extend function or method (as you probably saw in Backbone example).

    Hope it helps!

    0 讨论(0)
提交回复
热议问题