Javascript prototypal inheritance - descendants override each other

前端 未结 1 1839
清酒与你
清酒与你 2020-12-23 18:34

I\'m creating two objects (Inherits), both inherit from Base. The second object\'s property assignment overrides the value in the first object.

Any thoughts? How to

相关标签:
1条回答
  • 2020-12-23 19:10

    Problem(s)

    • The Base class constructor is called once and only once.
      this.prototype = new baseClass;
    • Privileged methods will refer to the same this object across instances because those methods are created within the constructor (which was called only once).

    Problem(s) based on a matter of opinion

    • You should try to avoid modifying native prototypes (ie. Function.prototype) if you plan on working alongside JavaScript that you do not own.

    Solution

    • Call the constructor and parent constructor(s) for each new instance that is created.
    • Inherit the parent prototype chain (not a instance of a the parent class).
    • Maintain a 1:1 ratio of number of instances created to the number of calls the constructor(s) in the prototype chain.

    Final solution to Max's problem

    Please pay special attention to the inherits function and the ParentClass.call(this, title); line. Constructors to look for are ParentClass and ChildClass

    /**
     * Allows a child constructor to safely inherit the parent constructors prototype chain.
     * @type {Function}
     * @param {!Function} childConstructor
     * @param {!Function} parentConstructor
     */
    function inherits(childConstructor, parentConstructor){
        var TempConstructor = function(){};
        TempConstructor.prototype = parentConstructor.prototype; // Inherit parent prototype chain
    
        childConstructor.prototype = new TempConstructor(); // Create buffer object to prevent assignments directly to parent prototype reference.
        childConstructor.prototype.constructor = childConstructor; // Reset the constructor property back the the child constructor
    };
    
    //////////////////////////////////////
    /** @constructor */
    function ParentClass(title) {
        this.setTitle(title);
    
        var randId_ = Math.random();
        /** @return {number} */
        this.getPrivlegedRandId = function()
            {return randId_;};
    };
    
    /** @return {string} */
    ParentClass.prototype.getTitle = function()
        {return this.title_;};
    
    /** @param {string} value */
    ParentClass.prototype.setTitle = function(value)
        {this.title_ = value;};
    
    //////////////////////////////////////    
    /**
     * @constructor
     * @param {string} title
     * @param {string} name 
     */
    ChildClass = function (name, title) {
        ParentClass.call(this, title); // Call the parent class constructor with the required arguments
    
        this.setName(name);
    }
    inherits(ChildClass, ParentClass); // Inherit the parent class prototype chain.
    
    /** @return {string} */
    ChildClass.prototype.getName = function()
        {return this.name_;};
    
     /** @param {string} value */
    ChildClass.prototype.setName = function(value)
        {this.name_ = value;};
    

    Down the rabbit hole

    For those who are curious why that works vs simply memorizing it the inherits function.

    How properties are resolved using the prototype chain

    When a property is not found at the instance level, JavaScript will try to resolve the missing property by searching through the instance constructors prototype chain. If the property is not found in the first prototype object, it will search the parent prototype object and so on all the way up to Object.prototype. If it can't find it within Object.prototype then an error will be thrown.

    Calling the parent constructor from child constructor : Attempt #1

    // Bad
    var ChildConstructor = function(arg1, arg2, arg3){
        var that = new ParentConstructor(this, arg1, arg2, arg3);
        that.getArg1 = function(){return arg1};
        return that;
    }
    

    Any varible that is created using new ChildConstructor will return an instance of ParentConstructor. The ChildConstructor.prototype will be not be used.

    Calling the parent constructor from child constructor : Attempt #2

    // Good
    var ChildConstructor = function(arg1, arg2, arg3){
        ParentConstructor.call(this, arg1, arg2, arg3);
    }
    

    Now constructor and the parent constructor is called appropriately. However, only methods defined within the constructor(s) will exist. Properties on the parent prototypes will not be used because they have not yet been linked to the child constructors prototype.

    Inheriting the parent prototype : Attempt #1

    // Bad
    ChildConstructor.prototype = new ParentConstructor();
    

    The parent constructor will either be called only once or one too many times depending on whether or not ParentConstructor.call(this) is used.

    Inheriting the parent prototype attempt #2

    // Bad
    ChildConstructor.prototype = ParentConstructor.prototype;
    

    Though this technically works, any assignments to ChildConstructor.prototype will also be assigned to ParentConstructor.prototype because Objects are passed by reference and not by copy.

    Inheriting the parent prototype attempt #3

    // Almost there
    var TempConstructor = function(){};
    TempConstructor.prototype = ParentConstructor.prototype;
    ChildConstructor.prototype = new TempConstructor();
    

    This allows you to assign properties to ChildConstructor.prototype because it is an instance of a temporary anonymous function. Properties that are not found on the instance of TempConstructor will then check it's prototype chain for the property, so you have successfully inherited the parent prototype. The only problem is that ChildConstructor.prototype.constructor is now pointing to TempConstructor.

    Inheriting the parent prototype attempt #4

    // Good
    var TempConstructor = function(){};
    TempConstructor.prototype = ParentConstructor.prototype;
    ChildConstructor.prototype = new TempConstructor();
    ChildConstructor.prototype.constructor = ChildConstructor;
    

    All Together

    var ParentConstructor = function(){
    };
    
    
    var ChildConstructor = function(){
        ParentConstructor.call(this)
    };
    
    var TempConstructor = function(){};
    TempConstructor.prototype = ParentConstructor.prototype;
    ChildConstructor.prototype = new TempConstructor();
    ChildConstructor.prototype.constructor = ChildConstructor;
    

    You've successfully inherited from the parent class! Let's see if we can do better.

    The inherits function

    function inherits(childConstructor, parentConstructor){
        var TempConstructor = function(){};
        TempConstructor.prototype = parentConstructor.prototype; // Inherit parent prototype chain
    
        childConstructor.prototype = new TempConstructor(); // Create buffer object to prevent assignments directly to parent prototype reference.
        childConstructor.prototype.constructor = childConstructor; // Reset the constructor property back the the child constructor (currently set to TempConstructor )
    };
    
    
    var ParentConstructor = function(){
    };
    
    
    var ChildConstructor = function(){
        ParentConstructor.call(this)
    };
    inherits(ChildConstructor, ParentConstructor);
    
    0 讨论(0)
提交回复
热议问题