😍Introduction to Prototype Chain#
Prototype Object#
A simple beginning
In JS,
everything is based on objects
- The
prototype
of a function points to the prototype object- The
constructor
of the prototype object points to the function- Properties and methods defined on the prototype object can be shared and inherited by all associated instance objects
prototype
is unique to functions- The
__proto__
property is unique to objects
- When JS creates a function, it generates a
prototype
property that points to an object, which is the prototype object of that function.
This object contains a constructor
property that points to the function.
- When declaring an instance object of that function, the object implements a
__proto__
property that points to theprototype object (ha)
of thatinstance object
, so we can compare the function'sprototype
and the instance object's__proto__
, and the result is of coursetrue
.
// Create a function named Ha
function Ha(){
this.name='哈喇'
}
// Call Ha's prototype
Ha.prototype //{constructor:f}
// Declare an instance object
let ha = new Ha()
// Prototype object comparison
ha.__proto__ === Ha.prototype // true
// The constructor can be called directly through ha.constructor
ha.__proto__.constructor === ha.constructor
Prototype Chain#
The prototype chain is simple; just look at the following code.
ha.__proto__.__proto__.__proto__ // null
// Here’s an explanation of why it is null
ha.__proto__ points to the prototype object of Ha
ha.__proto__.__proto__ points to the prototype object of Object, which is the prototype of Ha
ha.__proto__.__proto__.__proto__, since everything is based on objects, does the prototype object above Object still exist? Of course not, so it returns null.
// Constructor inheritance
function Haha(name){
this.name=name
Ha.call(this)
}
// First declare an instance of Haha, call the instance's name property, the printed value is the value of the name property in Ha, because inheritance is used, so if the current instance constructor does not have the property, it will look up the name property of Ha's constructor. If it exists, it will print; if not, it will continue to find the name property of Object's constructor, which still does not exist, resulting in null.
let haha = new Haha()
haha.name // 哈喇
// First declare an instance of Haha and pass parameters, call the instance's name property, the printed value is Wang Laowu.
let haha = new Haha('王老五')
haha.name // 王老五
Relationship Between Constructor, Instance, and Prototype#
The constructor is a function used to create instances. Each instance has a prototype, and the prototype points to the prototype of the constructor.
// The prototype of ha is Ha.prototype
// The prototype of Ha.prototype is Object.prototype
function Ha(){} // Constructor
let ha=new Ha() // Instance
Ha.prototype.constructor === Ha //true The constructor's prototype's constructor points to the constructor itself
Inheritance#
Constructor Inheritance#
Use call() to point Parent's this to Child's instance to achieve inheritance.
Advantages
- Parameters can be passed to the parent class constructor when creating instances.
- Each subclass instance has its own copy of the parent class instance; modifying the parent class instance properties will not affect other subclasses inheriting the same parent class.
Disadvantages
- Only inherits properties and methods from the parent class constructor, cannot inherit content from the prototype object.
- Cannot reuse.
function Parent(){
this.name='father'
}
Parent.prototype.sayHello = function () {
return this.name
}
function Children(){
Parent.call(this)
}
const c = new Children()
c.name // father
c.sayHello // c.sayHello is not a function
Prototype Chain Inheritance#
Rewrite the prototype object
Advantages
- Function reuse, subclasses can use parent class properties and methods.
- Subclasses can directly access properties and methods on the parent class prototype object.
Disadvantages
- Cannot pass parameters to the parent class constructor.
- Parent class reference properties will be shared among all subclasses; if one subclass modifies the reference property, other subclasses will also be affected because they operate on the same memory address.
- Parent class private variables will be exposed in subclasses.
function Parent(){
this.name='father'
this.hobby = ['唱', '跳']
}
Parent.prototype.sayHello = function(){
return this.name
}
function Children(){}
Children.prototype = new Parent()
const c = new Children()
const c2 = new Children()
c.name // father
c.sayHello() // father
c.hobby.push('rap')
c.hobby // ['唱', '跳', 'rap']
c2.hobby // ['唱', '跳', 'rap']
Combination Inheritance#
Constructor + Prototype Chain
Advantages
- Inherits all properties and methods of the parent class instance and prototype object (excluding private).
- Avoids the problem of reference type properties being shared by all instances (prototype chain inheritance).
Disadvantages
- Calls the parent class instance twice, affecting performance.
function Parent(){
this.name='father'
}
Parent.prototype.sayHello = function(){
return this.name
}
function Children(){
Parent.call(this)
}
Children.prototype = new Parent()
Children.prototype.constructor = Children
const c = new Children()
c.name // father
c.sayHello() // father
Parasitic Inheritance#
Prototypal inheritance + enhancing objects
Advantages
- Inherits all properties and methods of the parent class instance and prototype object (excluding private).
- Avoids the problem of reference type properties being shared by all instances (prototype chain inheritance).
Disadvantages
- Calls the parent class instance twice, affecting performance.
function Parent(){
this.name='father'
}
Parent.prototype.sayHello = function(){
return this.name
}
function Children(){
Parent.call(this)
}
Children.prototype = new Parent()
Children.prototype.constructor = Children
const c = new Children()
c.name // father
c.sayHello() // father
Parasitic Inheritance#
Enhancing the functionality of the parent class instance in the subclass constructor
Advantages
- Avoids the problem of reference type properties being shared by all instances (prototype chain inheritance).
Disadvantages
- Cannot achieve function reuse; each subclass internally instantiates the parent class.
- Can access private variables of the parent class, which is essentially the subclass containing the parent class instance.
function Parent() {
this.name = 'father'
this.hobby = ['唱', '跳']
}
Parent.prototype.sayHello = function () {
return this.name
}
function Children() {
let parent = new Parent()
parent.sayBye = function Bye() {
return this
}
return parent
}
const c = new Children()
console.log(c.sayHello());
Parasitic Combination Inheritance#
Enhancing the functionality of the parent class instance in the subclass constructor
Advantages
- Avoids the problem of reference type properties being shared by all instances (prototype chain inheritance).
- Only calls the Parent constructor once.
Disadvantages
- Cannot achieve function reuse; each subclass internally instantiates the parent class.
- Can access private variables of the parent class, which is essentially the subclass containing the parent class instance.
function _extends(children, parent) {
let parent_prototype = Object.create(parent.prototype)
parent_prototype.constructor = children
children.prototype = parent_prototype
}
function Parent() {
this.name = 'father'
this.hobby = ['唱', '跳']
}
Parent.prototype.sayHello = function () {
return this.name
}
function Children() {
Parent.call(this)
}
_extends(Children, Parent)
const c = new Children()
console.log(c.sayHello()) // father