Why is it necessary to set the prototype constructor?

前端 未结 14 1744
孤城傲影
孤城傲影 2020-11-22 02:54

In the section about inheritance in the MDN article Introduction to Object Oriented Javascript, I noticed they set the prototype.constructor:

// correct the          


        
相关标签:
14条回答
  • 2020-11-22 03:07

    No need for sugared function 'classes' or using 'New' these days. Use object literals.

    The Object prototype is already a 'class'. When you define an object literal, it is already an instance of the prototype Object. These can also act as another object's prototype, etc.

    const Person = {
      name: '[Person.name]',
      greeting: function() {
        console.log( `My name is ${ this.name || '[Name not assigned]' }` );
      }
    };
    // Person.greeting = function() {...} // or define outside the obj if you must
    
    // Object.create version
    const john = Object.create( Person );
    john.name = 'John';
    console.log( john.name ); // John
    john.greeting(); // My name is John 
    // Define new greeting method
    john.greeting = function() {
        console.log( `Hi, my name is ${ this.name }` )
    };
    john.greeting(); // Hi, my name is John
    
    // Object.assign version
    const jane = Object.assign( Person, { name: 'Jane' } );
    console.log( jane.name ); // Jane
    // Original greeting
    jane.greeting(); // My name is Jane 
    
    // Original Person obj is unaffected
    console.log( Person.name ); // [Person.name]
    console.log( Person.greeting() ); // My name is [Person.name]
    

    This is worth a read:

    Class-based object-oriented languages, such as Java and C++, are founded on the concept of two distinct entities: classes and instances.

    ...

    A prototype-based language, such as JavaScript, does not make this distinction: it simply has objects. A prototype-based language has the notion of a prototypical object, an object used as a template from which to get the initial properties for a new object. Any object can specify its own properties, either when you create it or at run time. In addition, any object can be associated as the prototype for another object, allowing the second object to share the first object's properties

    0 讨论(0)
  • 2020-11-22 03:09

    Here's one example from MDN which I found very helpful to understand its uses.

    In JavaScript, we have async functions which returns AsyncFunction object. AsyncFunction is not a global object but one may retrieve it by using constructor property and utilize it.

    function resolveAfter2Seconds(x) {
      return new Promise(resolve => {
        setTimeout(() => {
          resolve(x);
        }, 2000);
      });
    }
    
    // AsyncFunction constructor
    var AsyncFunction = Object.getPrototypeOf(async function(){}).constructor
    
    var a = new AsyncFunction('a', 
                              'b', 
                              'return await resolveAfter2Seconds(a) + await resolveAfter2Seconds(b);');
    
    a(10, 20).then(v => {
      console.log(v); // prints 30 after 4 seconds
    });
    
    0 讨论(0)
  • 2020-11-22 03:10

    So far confusion is still there.

    Following the original example, as you have an existing object student1 as:

    var student1 = new Student("Janet", "Applied Physics");
    

    Suppose you don't want to know how student1 is created, you just want another object like it, you can use the constructor property of student1 like:

    var student2 = new student1.constructor("Mark", "Object-Oriented JavaScript");
    

    Here it will fail to get the properties from Student if the constructor property is not set. Rather it will create a Person object.

    0 讨论(0)
  • 2020-11-22 03:10

    Got a nice code example of why it is really necessary to set the prototype constructor..

    function CarFactory(name){ 
       this.name=name;  
    } 
    CarFactory.prototype.CreateNewCar = function(){ 
        return new this.constructor("New Car "+ this.name); 
    } 
    CarFactory.prototype.toString=function(){ 
        return 'Car Factory ' + this.name;
    } 
    
    AudiFactory.prototype = new CarFactory();      // Here's where the inheritance occurs 
    AudiFactory.prototype.constructor=AudiFactory;       // Otherwise instances of Audi would have a constructor of Car 
    
    function AudiFactory(name){ 
        this.name=name;
    } 
    
    AudiFactory.prototype.toString=function(){ 
        return 'Audi Factory ' + this.name;
    } 
    
    var myAudiFactory = new AudiFactory('');
      alert('Hay your new ' + myAudiFactory + ' is ready.. Start Producing new audi cars !!! ');            
    
    var newCar =  myAudiFactory.CreateNewCar(); // calls a method inherited from CarFactory 
    alert(newCar); 
    
    /*
    Without resetting prototype constructor back to instance, new cars will not come from New Audi factory, Instead it will come from car factory ( base class )..   Dont we want our new car from Audi factory ???? 
    */
    
    0 讨论(0)
  • 2020-11-22 03:14

    Does this serve any important purpose?

    Yes and no.

    In ES5 and earlier, JavaScript itself didn't use constructor for anything. It defined that the default object on a function's prototype property would have it and that it would refer back to the function, and that was it. Nothing else in the specification referred to it at all.

    That changed in ES2015 (ES6), which started using it in relation to inheritance hierarchies. For instance, Promise#then uses the constructor property of the promise you call it on (via SpeciesConstructor) when building the new promise to return. It's also involved in subtyping arrays (via ArraySpeciesCreate).

    Outside of the language itself, sometimes people would use it when trying to build generic "clone" functions or just generally when they wanted to refer to what they believed would be the object's constructor function. My experience is that using it is rare, but sometimes people do use it.

    Is it okay to omit it?

    It's there by default, you only need to put it back when you replace the object on a function's prototype property:

    Student.prototype = Object.create(Person.prototype);
    

    If you don't do this:

    Student.prototype.constructor = Student;
    

    ...then Student.prototype.constructor inherits from Person.prototype which (presumably) has constructor = Person. So it's misleading. And of course, if you're subclassing something that uses it (like Promise or Array) and not using class¹ (which handles this for you), you'll want to make sure you set it correctly. So basically: It's a good idea.

    It's okay if nothing in your code (or library code you use) uses it. I've always ensured it was correctly wired up.

    Of course, with ES2015 (aka ES6)'s class keyword, most of the time we would have used it, we don't have to anymore, because it's handled for us when we do

    class Student extends Person {
    }
    

    ¹ "...if you're subclassing something that uses it (like Promise or Array) and not using class..." — It's possible to do that, but it's a real pain (and a bit silly). You have to use Reflect.construct.

    0 讨论(0)
  • 2020-11-22 03:16

    It is necessary. Any class in class inheritance must has its own constructor, so as in prototype inheritance.It is also convenient for object construction. But the question is unnecessary and what is necessary is understanding in JavaScript world effect of calling function as constructor and rule of resolving object property.

    Effect of executing function as constructor with expression new <function name>( [ parameters] )

    1. a object whose type name is the function name is created
    2. inner properties in the function attaches to the created object
    3. property prototype of the function attaches automatically to the created object as prototype

    Rule of resolving property of object

    • The property will not only be sought on the object but on the prototype of the object, the prototype of the prototype, and so on until either a property with a matching name is found or the end of the prototype chain is reached.

    Basing on these underlying mechanisms, statement <constructor name>.prototype.constructor = <constructor name> equals in term of effect to attach constructor in constructor body with expression this.constructor = <constructor name>. The constructor will be resolved on the object if second utterance while on object's prototype if first utterance.

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