subclass.prototype = new superclass() vs. subclass = new superclass()

后端 未结 3 968
醉话见心
醉话见心 2020-12-28 23:59

I\'ve been instantiating subclasses in javascript using

object = new class ()

but I notice some people instantiate using

ob         


        
相关标签:
3条回答
  • 2020-12-29 00:19

    The difference is that when you do:

    var subclass = new superclass();
    

    you are creating an instance of superclass. subclass is just variable. You are not creating a sub-class (ie. making subclass inherit superclass). In the latter example, assuming subclass is a function, you are saying that all new instances of subclass should inherit (ie. sub-class) superclass.

    So:

    function superclass() {this.stuff="stuff";}
    function subclass() {}
    subclass.prototype = new superclass();
    alert(new subclass().this); // pops up "stuff"
    

    is prototypical inheritance.

    As for the new operator, it is used for creating an instance of built-in objects and user defined types. A user defined type is simply a function.

    Edit: When I wrote above that subclass inherits supertype using prototypical inheritance, I mean that all new instances of subclass inherit from one particular instance of superclass, not from the superclass type/function itself.

    0 讨论(0)
  • 2020-12-29 00:21

    Sharing a quick demo of Javascript inheritance after reading the mozilla doc

    function Employee (name, dept) {
        this.name = name || "";
        this.dept = dept || "";
    }
    
    function Programmer (name, projs) {
        Employee.call(this, name, "programming");
        this.projects = projs || [];
    }
    Programmer.prototype = new Employee;
    
    // demo dynamic inheritance
    Employee.prototype.leave = 10;
    
    var johnny = new Programmer("Johnny", ["C#","Java"]);
    alert("name: " + johnny.name + "\n"
          + "dept: " + johnny.dept + "\n"
          + "projects: " + johnny.projects + "\n"
          + "leave: " + johnny.leave);
    
    var mary = new Programmer("Mary", ["Javascript","Java"]);
    alert("name: " + mary.name + "\n"
          + "dept: " + mary.dept + "\n"
          + "projects: " + mary.projects + "\n"
          + "leave: " + mary.leave);
    
    alert("changing leave of all staff to 8");
    Employee.prototype.leave = 8;
    alert("Johnny leave: " + johnny.leave); // 8
    alert("Mary leave: " + mary.leave); // 8
    
    alert("cannot batch move staff to another department");
    Employee.prototype.dept = "sales";
    alert("Johnny dept: " + johnny.dept); // programming
    alert("Mary dept: " + mary.dept); // programming
    

    Demo in jsfiddle Demo with more debug in jsfiddle

    0 讨论(0)
  • 2020-12-29 00:23

    The code samples in your question reflect a couple of misunderstanding. Let's address them first:

    • class is a reserved keyword in Javascript. You cannot use class as the name of any variable or function. This is not because the Javascript language makes any use of the keyword, but because it was planned for a possible future use.
    • there are no real classes in Javascript. What you may have seen, is different flavors of attempts to simulate class inheritance by using the available inheritance mechanism in Javascript, which is based on prototype objects, sharing properties with linked instances
    • very important: the prototype property used in this inheritance mechanism is set on a function, not directly on objects

    Quoting Douglas Crockford in Chapter 5, Inheritance, of JavaScript: The Good Parts:

    Instead of having objects inherit directly from other objects, an unnecessary level of indirection is inserted such that objects are produced by constructor functions.

    (...)

    When a function is invoked with the constructor invocation pattern using the new prefix, this modifies the way in which the function is executed.

    Douglas Crockford then explains how the new operator could be implemented as a JavaScript function. This function makes use of several other functions defined in the book, so I rewrote it in a (somewhat) simpler form below:

    function createNew(constructor) {
      // a function to explain the new operator:
      //   var object = createNew(constructor);
      // is equivalent to
      //   var object = new constructor();
      //
      // param: constructor, a function
      // return: a new instance of the "constructor" kind of objects
    
      // step 1. create a new empty object instance
      //         linked to the prototype of provided constructor  
      var hiddenLink = function(){};
      hiddenLink.prototype = constructor.prototype;
      var instance = new hiddenLink(); // cheap trick here: using new to implement new
    
      // step 2. apply the constructor the new instance and get the result
      var result = constructor.apply(object); // make this a reference to instance within constructor
    
      // step 3. check the result, and choose whether to return it or the created instance
      if (typeof result === 'object') {
        return object;
      } else {
        return instance;
      } 
    }
    

    In simple English, if you call new constructor(), where constructor is a function, the operator creates a new object with a link to inherit properties from the constructor, applies the constructor function to it, and returns either the value returned by the constructor, or the new object in case the constructor returned something else which is not an object.

    At any time, before or after creating new instances using a custom constructor, you may modify the prototype (object) on the constructor (function):

    function constructor(){}  // the most simple constructor function: does nothing
    var before = new constructor();
    var different = new constructor();
    different.answer = "So long, and thanks for all the fish";
    
    constructor.prototype = {};             // set an empty object to the prototype property
    constructor.prototype.answer = 42;      // create a new property on prototype object
    constructor.prototype.answer = Math.PI; // replace an existing property
    
    var after = new constructor();
    

    Through the hidden link added to all objects created using this constructor (see "cheap trick" in createNew), the properties of the prototype object can be accessed on all these instances, unless overridden by properties defined on the objects directly.

    before.answer === Math.PI; // true
    after.answer === Math.PI;  // true
    different.answer === "So long, and thanks for all the fish"; // true
    

    Using this newly acquired knowledge, how would you create a new "class" of objects that inherit all the properties of arrays, together with a new method empty() to remove all elements?

    First, there are no classes in Javascript, so in order to create a new "class", I have to define a new constructor function. Let's call it CustomArray, with a capital C to follow the convention that constructor functions should start with a capital.

    function CustomArray(){}
    

    I can now create custom instances:

    var myArray = new CustomArray(); 
    

    Second, I want instances created with CustomArray to inherit Array properties:

    myArray.prototype = new Array(); // WRONG EXAMPLE: we must set CustomArray.prototype
    CustomArray.prototype = Array;   // WRONG EXAMPLE: prototype expects an object, Array is a function
    CustomArray.prototype = new Array(); // OK, BUT: the simpler form [] should be used instead
    CustomArray.prototype = [];
    

    Third, I want all instances created with CustomArray to have the empty() method:

    function empty(){
        // empty this array by setting its length to 0
        // function to be called in the context (this) of an array
        this.length = 0;
    }
    CustomArray.prototype.empty = empty; // set the function named empty to the property "empty" of the prototype
    

    Finally, I can rewrite the whole example in a more concise way:

    function CustomArray(){}
    CustomArray.prototype = [];
    CustomArray.prototype.empty = function(){ this.length = 0; }
    

    I can now create a custom array, set a couple of values and empty it:

    var myArray = new CustomArray();
    myArray[0] = "abc";
    myArray[1] = "def";
    myArray[2] = "ghi";
    myArray.empty();
    

    The issue is: the above code does not work as expected. Why? Because unlike in regular arrays, setting values in our custom array does not automagically increase the length property of the array. Likewise, calling empty() only sets the length property of our custom array to 0, it does not delete all the values within.

    Besides, we could not use the array literal syntax to initialize our custom array:

    var myArray = ["abc","def","ghi"]; // this creates a regular array
    

    All in all, it is important to understand how Javascript inheritance works, but you may often find it less useful than expected and there are simpler ways to achieve the same result, for example by using builder functions instead of constructors. We can solve the problem by using a builder function customizeArray to extend regular arrays:

    function customizeArray(array){
      array.empty = function(){
        this.length = 0;
      }; 
    }
    var myArray = ["abc","def","ghi"];
    customizeArray(myArray);
    myArray.empty();
    

    This code works as expected because myArray is a regular array in this case, extended with a new method named empty. The main advantage and drawback of this approach in comparison with using prototypes is that it modifies only the selected instance, and if you were working with lots of similar objects at the same time, setting this extra property would use more memory than setting a single shared property on a common prototype.

    To avoid that, you may be tempted to modify the Array prototype directly:

    Array.prototype.empty = function(){
      this.length = 0;
    };
    var myArray = ["abc","def","ghi"];
    myArray.empty();
    

    It works, but I would advise against it: you are attaching a custom property to every array instance, including all those created by those fancy libraries, plugins, frameworks, advertising and analytics scripts, all the code on your page. Needless to say that it may break something in a place where you cannot fix it.

    Edit: Interesting post on kangax's blog as a follow-up: "How ECMAScript 5 still does not allow to subclass an array"

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