ES6 uses the class keyword to define classes and the extends keyword to inherit classes. The super method must be called in the constructor constructor of the subclass to obtain the "this" object of the parent class. When calling super, you can pass parameters to the parent constructor. Subclasses can directly use the properties and methods of the parent class through the super object, or they can override the definitions in the parent class through properties or methods with the same name.
class Father { constructor () { this.surname = '王' this.money = Infinity } sayName () { console.log(`My surname is ${this.surname}.`) } } class Son extends Father { constructor (firstname) { super() this.firstname = firstname } sayName () { console.log(`My name is ${super.surname}${this.firstname}.`) } sayMoney () { console.log(`I have ${this.money} money.`) } } let Sephirex = new Son('撕葱') Sephirex.sayName() Sephirex.sayMoney()
Classes and inheritance in ES6 are essentially syntax sugar implemented using prototypes. The methods defined in the class are equivalent to defining methods on the prototype. Defining properties in the constructor method is equivalent to the constructor mode, and the super method is equivalent. To call the constructor of the parent class in the subclass. Let's continue to discuss the implementation of inheritance in ES5.
The basic pattern of prototype chain inheritance is to let the prototype object of the subtype point to an instance of the parent type, and then extend methods for its prototype.
function Person (name) { this.name = name this.likes = ['apple', 'orange'] } Person.prototype.sayName = function () { console.log(this.name) } function Worker () { this.job = 'worker' } Worker.prototype = new Person() Worker.prototype.sayJob = function () { console.log(this.job) } let Tom = new Worker() let Jerry = new Worker() Tom.likes.push('grape') console.log(Jerry.likes) // [ 'apple', 'orange', 'purple' ]
Principle: In the previous article, we discussed __proto__ and prototype. There is a __proto__ pointer in the instance of the subclass, which points to the prototype object of its constructor. The prototype of the subclass constructor points to an instance of the parent class, and the __proto__ in the parent class instance points to the prototype of the parent class constructor... In this way, a prototype chain is formed.
It should be noted that even if the reference type attribute in the parent class is defined in the constructor, it will still be shared by the subclass instance. This is because the prototype of the subclass constructor is actually an instance of the parent class, so the instance properties of the parent class naturally become the prototype properties of the subclass, and the prototype properties of reference type values are shared between instances.
Another problem with the prototype chain is that there is no way to pass parameters to the constructor of the parent class without affecting all object instances. Like the above example, when using Worker.prototype = new Person() to point the subclass prototype to the parent class instance, if initialization parameters are passed in, the instance name attributes of all subclasses will be the passed in parameters. If no parameters are passed here, there will be no way to pass parameters to the parent class constructor later. Therefore, the prototype chain inheritance pattern is rarely used alone.
Borrowing constructors can solve the problem of shared reference type attributes. The so-called "borrowing" a constructor is to call the constructor of the parent class in the constructor of the subclass--don't forget that the pointer of this in the function has nothing to do with where the function is defined, but only with where it is called. We can use call or apply to call the constructor of the parent class on the subclass instance to obtain the properties and methods of the parent class, similar to calling the super method in the ES6 subclass constructor.
function Person (name) { this.name = name this.likes = ['apple', 'orange'] } function Worker (name) { Person.call(this, name) this.job = 'worker' } let Tom = new Worker('Tom') Tom.likes.push("grape") let Jerry = new Worker('Jerry') console.log(Tom.likes) // [ 'apple', 'orange', 'grape' ] console.log(Jerry.likes) // [ 'apple', 'orange' ]
The problem with simply using the constructor is that the function cannot be reused, and the subclass cannot obtain the attributes and methods on the parent class prototype.
Combined inheritance borrows constructors to define instance properties and uses prototype chain sharing methods. Combining inheritance combines the prototype chain mode and the borrowed constructor, thereby leveraging the strengths of both and making up for their respective shortcomings. It is the most commonly used inheritance mode in js.
function Person (name) { this.name = name this.likes = ['apple', 'orange'] } Person.prototype.sayName = function () { console.log(this.name) } function Worker (name, job) { Person.call(this, name) // 第二次调用 Person() this.job = job } Worker.prototype = new Person() // 第一次调用 Person() Worker.prototype.constructor = Worker Worker.prototype.sayJob = function () { console.log(this.job) } let Tom = new Worker('Tom', 'electrician') Tom.likes.push('grape') console.log(Tom.likes) // [ 'apple', 'orange', 'grape' ] Tom.sayName() // Tom Tom.sayJob() // electrician let Jerry = new Worker('Jerry', 'woodworker') console.log(Jerry.likes) // [ 'apple', 'orange' ] Jerry.sayName() // Jerry Jerry.sayJob() // woodworker
Combined inheritance is not without its shortcomings, that is, the inheritance process will call the parent class constructor twice. When the Person constructor is called for the first time, Worker.prototype will get two attributes: name and likes; when the Worker constructor is called, the Person constructor will be called again, and this time the instance attributes name and likes are directly created, covering Two properties with the same name in the prototype.
The following object function was recorded in an article by Douglas Crockford. Inside the object function, a temporary constructor is first created, then the passed in object is used as the prototype of this constructor, and finally a new instance of this temporary type is returned. Essentially, object() performs a shallow copy of the object passed into it. This inheritance method is equivalent to copying the properties and methods of the parent type to the subtype, and then adding respective properties and methods to the subtype.
This method will also share attributes of reference type values.
function object(o){ function F(){} F.prototype = o; return new F(); } let Superhero = { name: 'Avenger', skills: [], sayName: function () { console.log(this.name) } } let IronMan = object(Superhero) IronMan.name = 'Tony Stark' IronMan.skills.push('fly') let CaptainAmerica = object(Superhero) CaptainAmerica.name = 'Steve Rogers' CaptainAmerica.skills.push('shield') IronMan.sayName() // Tony Stark console.log(IronMan.skills) // [ 'fly', 'shield' ]
The Object.create() method is used to standardize prototypal inheritance in ES5. This method accepts two parameters: an object to be used as the prototype of the new object and (optionally) an object to define additional properties for the new object. Object.create() behaves the same as the object() method when one argument is passed in. The second parameter of the Object.create() method has the same format as the second parameter of the Object.defineProperties() method.
let CaptainAmerica = Object.create(Superhero, { name: { value: 'Steve Rogers', configurable: false } })
Parasitic inheritance is easy to understand. It is just a factory function that encapsulates the inheritance process. Since methods are defined directly on the object, methods added by parasitic inheritance cannot be reused.
function inherit(parent){ var clone = Object.create(parent) clone.name = 'hulk' clone.sayHi = function(){ console.log("hi") } return clone } let Hulk = inherit(Superhero) Hulk.sayName() // hulk Hulk.sayHi() // hi
As mentioned earlier, combined inheritance is the most commonly used inheritance method in js, but the disadvantage is that the constructor of the parent class will be called twice. Parasitic compositional inheritance can solve this problem and is considered the most ideal inheritance method for objects containing reference type values.
The basic idea of parasitic combined inheritance is that it is not necessary to call the constructor of the parent class in order to specify the prototype of the subclass. All that is needed is a copy of the prototype of the parent class. Parasitic compositional inheritance is to inherit properties by borrowing constructors, and then use parasitic inheritance to inherit the prototype of the parent class.
function inheritPrototype(subType, superType){ var prototype = Object.create(superType.prototype) prototype.constructor = subType subType.prototype = prototype } function Person (name) { this.name = name this.likes = ['apple', 'orange'] } Person.prototype.sayName = function () { console.log(this.name) } function Worker (name, job) { Person.call(this, name) this.job = job } inheritPrototype(Worker, Person) Worker.prototype.sayJob = function () { console.log(this.job) }
The above is the detailed content of What are the inheritance methods in js?. For more information, please follow other related articles on the PHP Chinese website!