Six common inheritance methods in JavaScript
Foreword
An important aspect of object-oriented programming is the inheritance of objects. By inheriting the B object, the A object can directly possess all the properties and methods of the B object . This is very useful for code reuse.
Most object-oriented programming languages implement object inheritance through "classes". Traditionally, inheritance in the JavaScript language is not achieved through classes (ES6 introduced the class syntax), but through "prototypes". So what are the common inheritance methods in JS?
Method 1. Prototype chain inheritance
The key to this approach is that the prototype of the subtype is an instance object of the supertype.
// parent type
function Person(name, age) {
this.name = name,
this.age = age,
this.play = [1, 2, 3]
this.setName = function () {}
}
Person.prototype.setAge = function () {}
//Subtype
function Student(price) {
this.price = price
this.setScore = function () {}
}
Student.prototype = new Person() // The prototype of the subtype is an instance object of the supertype
var s1 = new Student(15000)
var s2 = new Student(14000)
console.log(s1,s2)
But the essence of this approach is to point the prototype of the subclass to the instance of the parent class, so the instance of the subclass can access the Student.prototype, which is the instance of Person, through __proto__, so that the parent class can be accessed. The private method of the parent class can be obtained by __proto__ pointing to the prototype of the parent class . Therefore, the private and public methods and properties of the parent class are treated as public properties of the subclass.
The subclass inherits the properties and methods of the parent class by using the private properties and public methods of the parent class as its own public properties and methods . We all know that when operating basic data types, the operation is the value, and when operating the reference data type The operation is the address. If there is an attribute of the reference type in the private attribute of the parent class, it will be used as a public attribute when it is inherited by the subclass, so that when the subclass 1 operates this attribute, it will affect the subclass 2.
s1.play.push(4)
console.log(s1.play, s2.play)
console.log(s1.__proto__ === s2.__proto__)//true
console.log(s1.__proto__.__proto__ === s2.__proto__.__proto__)//true
The play attribute in s1 changes, and at the same time, the play attribute in s2 also changes.
Another point to note is that when we need to add new methods to subclasses or override methods of parent classes, remember to put them after the statement that replaces the prototype.
function Person (name, age) {
this.name = name,
this.age = age
}
Person.prototype.setAge = function () {
console.log("111")
}
function Student (price) {
this.price = price
this.setScore = function () { }
}
// Student.prototype.sayHello = function () { }//It is invalid to write prototype methods and properties of subclasses here,
//Because it will change the pointer of the prototype, it should be placed after re-designation
Student.prototype = new Person()
Student.prototype.sayHello = function () { }
var s1 = new Student(15000)
console.log(s1)
Features :
- The parent class adds prototype methods/prototype properties, which can be accessed by subclasses
- Simple and easy to implement
Disadvantages :
- Multiple inheritance is not possible
- All properties from the prototype object are shared by all instances
- When creating a subclass instance, you cannot pass parameters to the parent class constructor
- To add properties and methods to subclasses, they must be Student.prototype = new Person()executed after , not in the constructor
Method 2: Borrowing Constructor Inheritance
The key to this method is: in the subtype constructor, the general call() calls the supertype constructor
function Person(name, age) {
this.name = name,
this.age = age,
this.setName = function () {}
} }
Person.prototype.setAge = function () {}
function Student(name, age, price) {
Person.call(this, name, age) // equivalent to: this.Person(name, age)
/*this.name = name
this.age = age*/
this.price = price
} }
var s1 = new Student('Tom', 20, 15000)
This method only implements partial inheritance. If the prototype of the parent class has methods and properties, the subclass cannot get these methods and properties.
console.log(s1.setAge())//Uncaught TypeError: s1.setAge is not a function
Features :
- Solved the problem of subclass instances sharing parent class reference properties in prototype chain inheritance
- When creating an instance of a subclass, you can pass parameters to the parent class
- Can achieve multiple inheritance (call multiple parent class objects)
Disadvantages :
- An instance is not an instance of the parent class, just an instance of the child class
- Can only inherit the instance properties and methods of the parent class, not the prototype properties and methods
- Function reuse cannot be achieved, each subclass has a copy of the parent class instance function, which affects performance
Method 3: Prototype chain + borrowed constructor combined inheritance
The key to this method is that: by calling the parent class structure, inheriting the properties of the parent class and retaining the advantages of passing parameters, and then by using the parent class instance as the child class prototype to achieve function reuse.
function Person (name, age) {
this.name = name,
this.age = age,
this.setAge = function () { }
}
Person.prototype.setAge = function () {
console.log("111")
}
function Student (name, age, price) {
Person.call(this, name, age)
this.price = price
this.setScore = function () { }
}
Student.prototype = new Person()
Student.prototype.constructor = Student//Combined inheritance also needs to fix what the constructor points to
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000)
var s2 = new Student('Jack', 22, 14000)
console.log(s1)
console.log(s1.constructor) //Student
console.log(p1.constructor) //Person
This method combines the advantages of prototype chain inheritance and constructor functions, and is the most commonly used inheritance pattern in JavaScript. However, there is also the disadvantage that in any case, the constructor will be called twice: once when the subtype prototype is created, and once inside the subtype constructor, the subtype will eventually contain all instances of the supertype object. properties, but we had to override those properties when calling the subclass constructor.
Advantages :
- Instance properties/methods can be inherited, as well as prototype properties/methods
- There is no reference property sharing problem
- Passable
- Functions can be reused
Disadvantages :
- The parent class constructor is called twice, and two instances are generated
Method 4: Combined Inheritance Optimization 1
In this way, the parent class prototype and the child class prototype point to the same object, and the child class can inherit the public methods of the parent class as its own public methods, and will not initialize the instance methods/properties twice, avoiding the shortcomings of combined inheritance .
function Person (name, age) {
this.name = name,
this.age = age,
this.setAge = function () { }
}
Person.prototype.setAge = function () {
console.log("111")
}
function Student (name, age, price) {
Person.call(this, name, age)
this.price = price
this.setScore = function () { }
}
Student.prototype = Person.prototype
Student.prototype.sayHello = function () { }
var s1 = new Student('Tom', 20, 15000)
console.log(s1)
But this way there is no way to distinguish whether the object is a subclass or a superclass instantiation
console.log(s1 instanceof Student, s1 instanceof Person)//true true
console.log(s1.constructor)//Person
Advantages :
- Does not initialize instance methods/properties twice, avoiding the disadvantages of combined inheritance
Disadvantages :
- There is no way to distinguish whether the instance is created by the subclass or the superclass, and the constructors of the subclass and the superclass are the same.
Method 5: Combined inheritance optimization 2
With the help of prototypes, objects can be created based on existing objects, and var B = Object.create(A)the A object is used as the prototype to generate the B object. B inherits all the properties and methods of A.
function Person (name, age) {
this.name = name,
this.age = age
}
Person.prototype.setAge = function () {
console.log("111")
}
function Student (name, age, price) {
Person.call(this, name, age)
this.price = price
this.setScore = function () { }
}
Student.prototype = Object.create(Person.prototype)
Student.prototype.constructor = Student
var s1 = new Student('Tom', 20, 15000)
console.log(s1 instanceof Student, s1 instanceof Person) // true true
console.log(s1.constructor) //Student
console.log(s1)
Likewise, Student inherits all the properties and methods of the Person prototype object. For now, the most perfect inheritance method!
Method 6: Inheritance of classes in ES6
The class keyword was introduced in ES6. Class can implement inheritance through the extends keyword, and can also define static methods of the class through the static keyword. This is much clearer and more convenient than ES5's implementation of inheritance by modifying the prototype chain.
The essence of ES5 inheritance is to first create the instance object this of the subclass, and then add the method of the parent class to this (Parent.apply(this)). The inheritance mechanism of ES6 is completely different. The essence is to first add the properties and methods of the instance object of the parent class to this (so the super method must be called first), and then use the constructor of the subclass to modify this.
It should be noted that the class keyword is just syntactic sugar for prototypes, and JavaScript inheritance is still implemented based on prototypes .
class Person {
//call the constructor of the class
constructor (name, age) {
this.name = name
this.age = age
}
//define the general method
showName() {
console.log("Call the method of the parent class")
console.log(this.name, this.age);
}
}
let p1 = new Person('kobe', 39)
console.log(p1)
//define a subclass
class Student extends Person {
constructor (name, age, salary) {
super(name, age)//Call the constructor of the parent class through super
this.salary = salary
}
showName () {//Define the method in the subclass itself
console.log("Call the subclass's method")
console.log(this.name, this.age, this.salary);
}
}
let s1 = new Student('wade', 38, 1000000000)
console.log(s1)
s1.showName()
Advantages :
- The syntax is simple and easy to understand, and the operation is more convenient
Disadvantages :
- Not all browsers support the class keyword