Prototype chain inheritance
function Parent() {
this.users = ['userA'.'userB'.'userC']}// key point 1: person1 and person2 can inherit properties and methods from constructor 'prototype';
Parent.prototype.getUsers = function() {
console.log(this.users)
}
Parent.prototype.sex = 'male'
Parent.prototype.list = []
function Child() {
this.age = 18
}
Child.prototype = new Parent()
var person1 = new Child()
// Key point 2: properties of reference types in constructors and prototype objects are shared (list and users)
person1.users.push('userD')
person1.list.push('userF')
// Key point 3: there is no sex attribute for person1 instance itself, but the sex attribute is actually added to person1
person1.sex = 'woman'
var person2 = new Child()
console.log(person1.sex, person2.sex)
console.log(person1, person2)
Copy the code
As shown in the figure, the advantages and disadvantages of prototype chain inheritance can be clearly seen
- Advantages: You can inherit both constructors and prototype objects
prototype
Properties and methods on; - Disadvantages: Attributes of reference types are shared by all instances (actually all attributes, except attributes of primitive types that cannot be modified by the instance);
Constructor inheritance (classical inheritance)
function Parent(user) {
this.users = ['userA'.'userB'.'userC']
this.users.push(user)
}
// Key point: the prototype property cannot be inherited
Parent.prototype.sex = 'male'
function Child(user) {
// Key point: you can pass arguments to the parent class
Parent.call(this, user)
this.age = 18
}
var person1 = new Child('person1')
// Key: The Users attribute is not shared
person1.users.push('userD')
var person2 = new Child('person2')
console.log(person1.age, person2.age)
console.log(person1.sex, person2.sex)
console.log(person1.users, person2.users)
Copy the code
From this example, we can see that constructor inheritance is compared to prototype chain inheritance:
- Advantages:
- The referenced type properties are prevented from being shared by all instances;
- You can pass arguments to a parent class
- Disadvantages: Can only inherit properties and methods from constructors, not prototype objects
prototype
Properties and methods on;
Combination of inheritance
function Parent(user) {
this.users = ['userA'.'userB'.'userC']
user && this.users.push(user)
}
Parent.prototype.sex = 'male'
function Child(user) {
Parent.call(this, user)
this.age = 18
}
Call (this, user) and new Parent() inherit attributes from the Parent constructor repeatedly
Child.prototype = new Parent()
var person1 = new Child('person1')
person1.users.push('userD')
var person2 = new Child('person2')
console.log(person1, person2)
Copy the code
Combination of inheritance
- Advantages: combining the advantages of prototype chain inheritance and constructors;
- Disadvantages:
- Redundant attributes are created (users as shown)
- The Parent method is called twice (new Parent(), parent.call (this, user))
Combinatorial inheritance optimization 1
- In view of the above question 1, we first review the knowledge point of prototype chain
// The instance object's prototype '__proto__' points to the constructor's prototype object
new Parent().__proto__ === Parent.prototype // true
The 'constructor' property of the constructor prototype object points to the constructor itself
Child.prototype.constructor === Child // true
Copy the code
Then we can make the optimization code as follows:
function Parent(user) {
this.users = ['userA'.'userB'.'userC']
this.users.push(user)
}
Parent.prototype.sex = 'male'
function Child(user) {
Parent.call(this, user)
this.age = 18
}
Parent.call(this, user) already inherits properties from the Parent constructor, just need to inherit properties from the Parent prototype object
Child.prototype = Parent.prototype
/ / expectations: Child. Prototype. Constructor = = = Child; Actual: Child. Prototype. Constructor = = = the Parent
console.log(Child.prototype.constructor === Child) // false
console.log(Child.prototype.constructor === Parent) // true
var person1 = new Child('person1')
person1.users.push('userD')
var person2 = new Child('person2')
console.log(person1, person2)
Copy the code
The Parent constructor is called only once. The Parent constructor is called only once. Together before the prototype chain of knowledge, we expect: Child. The prototype. The constructor = = = the Child, but in fact the Child. The prototype. The constructor = = = the Parent
Let’s continue to refine the code
Combinatorial inheritance optimization 2
function Parent(user) {
this.users = ['userA'.'userB'.'userC']
this.users.push(user)
}
Parent.prototype.sex = 'male'
function Child(user) {
Parent.call(this, user)
this.age = 18
}
// Query: Is there a problem, how to optimize
Child.prototype = Parent.prototype
// Optimize the code
Child.prototype.constructor = Child
var person1 = new Child('person1')
var person2 = new Child('person2')
console.log(Child.prototype.constructor === Child) // true
console.log(person1.constructor === Child) // true
Copy the code
The code is almost done at this point, but there is a problem here. We know that assigning to a similar object is a shallow copy, so modifying Child’s prototype property overrides the Parent. Prototype property and adding a new property
In the following example, we want the Child to inherit the attributes of the Parent, without directly modifying the attributes of the Parent itself, without affecting the instance of the Parent
function Parent(user) {
this.users = ['userA'.'userB'.'userC']
this.users.push(user)
}
Parent.prototype.sex = 'male'
function Child(user) {
Parent.call(this, user)
this.age = 18
}
Child.prototype = Parent.prototype
Child.prototype.constructor = Child
// overrides the same attribute in parent
Child.prototype.sex = 'woman'
Child.prototype.weight = '70kg'
var person = new Child('person')
var parent = new Parent('Parent')
console.log(person.sex, person.weight) / / female, 70 kg
// Expected: male, undefined; Actual female, 70kg
console.log(parent.sex, parent.weight) / / female, 70 kg
Copy the code
Combinatorial inheritance optimization 3
Parent. Call (this, user) already inherits properties from Parent’s constructor, so it only needs to inherit properties from Parent’s prototype object
Parent.prototype = new Parent().__proto__
Copy the code
function Parent(user) {
this.users = ['userA'.'userB'.'userC']
this.users.push(user)
}
Parent.prototype.sex = 'male'
function Child(user) {
Parent.call(this, user)
this.age = 18
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
// overrides the same attribute in parent
Child.prototype.sex = 'woman'
Child.prototype.weight = '70kg'
var person = new Child('person')
var parent = new Parent('Parent')
console.log(person.sex, person.weight) / / female, 70 kg
console.log(parent.sex, parent.weight) / / male, undefined
Copy the code
In addition toObject.create(obj)
Object.create(obj) creates a prototype reference to an obj Object
/ / Object simulation. The create (obj)
function createObj(obj) {
var prototype = obj
function Fn() {}
Fn.prototype = prototype
// new Fn().__proto__ == Fn.prototype
return new Fn()
}
Copy the code
Modifying the previous code, as shown below, we get exactly the same result
function Parent(user) {
this.users = ['userA'.'userB'.'userC']
this.users.push(user)
}
Parent.prototype.sex = 'male'
function Child(user) {
Parent.call(this, user)
this.age = 18
}
function createObj(obj) {
var prototype = obj
function Fn() {}
Fn.prototype = prototype
return new Fn()
}
Child.prototype = createObj(Parent.prototype)
Child.prototype.constructor = Child
// overrides the same attribute in parent
Child.prototype.sex = 'woman'
Child.prototype.weight = '70kg'
var person = new Child('person')
var parent = new Parent('Parent')
console.log(person.sex, person.weight) / / female, 70 kg
console.log(parent.sex, parent.weight) / / male, undefined
Copy the code
conclusion
- This article describes the various approaches to inheritance, as well as the pros and cons
- How to implement function inheritance step by step, and how to optimize
- This article involves the knowledge of prototype chain, basic data types, reference data types, Object.create() and so on