Javascript Prototypal Inheritance?

后端 未结 5 2003
日久生厌
日久生厌 2021-02-04 05:43

I\'been doing some inheritance in js in order to understand it better, and I found something that confuses me.

I know that when you call an \'constructor function\' with

相关标签:
5条回答
  • 2021-02-04 05:55

    I know it's already been answered but, there's a better way to do inheritance. Calling a constructor just for the purpose of inheritance is not desirable. One of the undesired effects is.

    function Base() {this.a = "A"}
    function Child() {this.b = "B"};
    
    Child.prototype = new Base();
    

    Now You've added property "a" to the prototype of Child that you didn't intend to.

    Here's the right way (I didn't invent this, Ext-JS and other libs use this)

    // This is used to avoid calling a base class's constructor just to setup inheritance.
    function SurrogateCtor() {}
    
    /**
     * Sets a contructor to inherit from another constructor
     */
    function extend(BaseCtor, DerivedCtor) {
      // Copy the prototype to the surrogate constructor
      SurrogateCtor.prototype = BaseCtor.prototype;
      // this sets up the inheritance chain
      DerivedCtor.prototype = new SurrogateCtor();
      // Fix the constructor property, otherwise it would point to the BaseCtor
      DerivedCtor.prototype.constructor = DerivedCtor;
      // Might as well add a property to the constructor to 
      // allow for simpler calling of base class's method
      DerivedCtor.superclass = BaseCtor;
    }
    
    function Base() {
      this.a = "A";
    }
    
    Base.prototype.getA = function() {return this.a}
    
    function Derived() {
      Derived.superclass.call(this);  // No need to reference the base class by name
      this.b = "B";
    }
    
    extend(Base, Derived);
    // Have to set methods on the prototype after the call to extend
    // otherwise the prototype is overridden;
    Derived.prototype.getB = function(){return this.b};
    var obj = new Derived();
    

    An even easier way is to add a third parameter to extend where you specify the method of the derived class so that you don't have to call extend and then add methods to the prototype

    extend(BaseCtor, DerivedCtor, {
      getB: function() {return this.b}
    });
    

    Then there are many other things you could do for syntactic sugar.

    Blogged about it: http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

    0 讨论(0)
  • 2021-02-04 05:56

    1) Why if all new objects contain a reference to the creator function's prototype, fido.prototype is undefined?

    All new objects do hold a reference to the prototype that was present on their constructor at the time of construction. However the property name used to store this reference is not prototype as it is on the constructor function itself. Some Javascript implementations do allow access to this 'hidden' property via some property name like __proto__ where others do not (for example Microsofts).

    2) Is the inheritance chain [obj] -> [constructor] -> [prototype] instead of [obj] -> [prototype] ?

    No. Take a look at this:-

    function Base() {}
    Base.prototype.doThis = function() { alert("First"); }
    
    function Base2() {}
    Base2.prototype.doThis = function() { alert("Second"); }
    
    function Derived() {}
    Derived.prototype = new Base()
    
    var x = new Derived()
    
    Derived.prototype = new Base2()
    
    x.doThis();
    

    This alerts "First" not Second. If the inheritance chain went via the constructor we would see "Second". When an object is constructed the current reference held in the Functions prototype property is transfered to the object hidden reference to its prototype.

    3) is the 'prototype' property of our object (fido) ever checked? if so... why is 'deathBite' undefined (in the last part)?

    Assigning to an object (other than a Function) a property called prototype has no special meaning, as stated earlier an object does not maintain a reference to its prototype via such a property name.

    0 讨论(0)
  • 2021-02-04 06:01

    You cannot change an object's prototype once it's been instantiated with new.

    In your example above, lines like

    fido.prototype = new KillerDog();
    

    simply creates a new attribute named prototype on the object fido, and sets that attribute to a new KillerDog object. It's no different than

    fido.foo = new KillerDog();
    

    As your code stands...

    // Doesn't work because objects can't be changed via their constructors
    fido.deathBite();
    
    // Does work, because objects can be changed dynamically, 
    // and Javascript won't complain when you use prototype 
    //as an object attribute name
    fido.prototype.deathBite();
    

    The special prototype behavior applies only to constructors in javascript, where constructors are functions that will be called with new.

    0 讨论(0)
  • 2021-02-04 06:12

    Worth noting that in ECMAScript 5 (i.e. the latest version of the JavaScript language) you can get access to the internal [[Prototype]] property of an instance via Object.getPrototypeOf:

    Object.getPrototypeOf(fido) === Dog.prototype
    
    0 讨论(0)
  • 2021-02-04 06:13

    Answer by numbers to your questions:

    1. Object's prototype property is not called prototype. The standard uses [[prototype]] to designate it. Firefox makes this property public under the name of __proto__.
    2. The inheritance chain is [obj][prototype object]. Your original assumption ([obj][constructor][prototype]) is incorrect and you can easily disprove it by modifying constructor and/or constructor.prototype, and checking what methods can be called on your [obj] — you will discover that these modifications do not change anything.
    3. prototype property on objects are not checked and not used. You can set it to whatever you like. JavaScript uses it on function objects only during the object construction.

    To demonstrate the #3 here is the code from Dojo:

    dojo.delegate = dojo._delegate = (function(){
      // boodman/crockford delegation w/ cornford optimization
      function TMP(){}
      return function(obj, props){
        TMP.prototype = obj;
        var tmp = new TMP();
        if(props){
          dojo._mixin(tmp, props);
        }
        return tmp; // Object
      }
    })();
    

    As you can see it takes advantage of the fact that prototype is used only in one place by reusing the same function TMP for all delegated objects with different prototypes. In fact prototype is assigned directly before invoking the function with new, and it will be changed after that not affecting any created objects.

    You can find the object created sequence in my answer to Relation between [[Prototype]] and prototype in JavaScript.

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