SoFunction
Updated on 2025-02-28

Detailed explanation of JavaScript inheritance (III)

Note: The implementation of jClass in this chapter is referencedSimple JavaScript InheritanceHow to do it.

First, let's review the examples introduced in Chapter 1:

 function Person(name) {
= name;
}
= {
getName: function() {
return ;
}
}

function Employee(name, employeeID) {
= name;
= employeeID;
}
= new Person();
= function() {
return ;
};
var zhang = new Employee("ZhangSan", "1234");
(()); // "ZhangSan"

 

Fixed the constructor pointing error

 

From the description of constructor in the previous article, we know that the constructor of the Employee instance will have a pointing error, as shown below:

 var zhang = new Employee("ZhangSan", "1234");
( === Employee); // false
( === Object); // true
We need a simple correction:
 function Employee(name, employeeID) {
= name;
= employeeID;
}
= new Person();
= Employee;
= function() {
return ;
};
var zhang = new Employee("ZhangSan", "1234");
( === Employee); // true
( === Object); // false

 

Instantiating Person when creating Employee class is inappropriate

 

But on the other hand, we must rely on this mechanism to achieve inheritance. The solution is to not initialize the data in the constructor, but to provide a prototype method (such as init) to initialize the data.

 // Empty constructor
function Person() {
}
= {
init: function(name) {
= name;
},
getName: function() {
return ;
}
}
// Empty constructor
function Employee() {
}
// The stage of creating a class will not initialize the data of the parent class,becausePersonis an empty constructor
= new Person();
= Employee;
= function(name, employeeID) {
= name;
= employeeID;
};
= function() {
return ;
};
In this way, the init function must be manually called after instantiating an object, as follows:
 var zhang = new Employee();
("ZhangSan", "1234");
(()); // "ZhangSan"

 

How to automatically call the init function?

 

Two effects must be achieved. Do not call the init function when constructing the class and automatically call the init function when instantiating the object. It seems that we need to have a status flag when calling an empty constructor.

 // Create a global status marker - Is it currently in the construction stage of the class?
var initializing = false;
function Person() {
if (!initializing) {
(this, arguments);
}
}
= {
init: function(name) {
= name;
},
getName: function() {
return ;
}
}
function Employee() {
if (!initializing) {
(this, arguments);
}
}
// Sign the current entry class creation stage,Will not callinitfunction
initializing = true;
= new Person();
= Employee;
initializing = false;
= function(name, employeeID) {
= name;
= employeeID;
};
= function() {
return ;
};

// When initializing the class instance,Automatically call the prototype function of the classinit,And toinitPass parameters in
var zhang = new Employee("ZhangSan", "1234");
(()); // "ZhangSan"
But in this way, global variables must be introduced, which is a bad signal.

 

How to avoid introducing global variable initializing?

 

We need to introduce a global function to simplify the creation process of the class, while encapsulating internal details to avoid introducing global variables.

 // Is it currently in the stage of creating a class?
var initializing = false;
function jClass(baseClass, prop) {
// Only one parameter is accepted - jClass(prop)
if (typeof (baseClass) === "object") {
prop = baseClass;
baseClass = null;
}
// The class created by this call(Constructor)
function F() {
// If you are currently in the instantiation class stage,Then callinitPrototype functions
if (!initializing) {
(this, arguments);
}
}
// If this class needs to be extended from other classes
if (baseClass) {
initializing = true;
= new baseClass();
= F;
initializing = false;
}
// Override the function of the same name of the parent class
for (var name in prop) {
if ((name)) {
[name] = prop[name];
}
}
return F;
};
Use jClass function to create classes and inherit classes:
 var Person = jClass({
init: function(name) {
= name;
},
getName: function() {
return ;
}
});
var Employee = jClass(Person, {
init: function(name, employeeID) {
= name;
= employeeID;
},
getEmployeeID: function() {
return ;
}
});

var zhang = new Employee("ZhangSan", "1234");
(()); // "ZhangSan"
OK, now the way to create and instantiate classes looks much more elegant. However, there are obvious flaws here. Employee's initialization function init cannot call the parent class's method of the same name.

 

How to call the parent class's method with the same name?

 

We can point to the prototype of the parent class (constructor) by providing a base property to the instantiation object, as follows:

 // Is it currently in the stage of creating a class?
var initializing = false;
function jClass(baseClass, prop) {
// Only one parameter is accepted - jClass(prop)
if (typeof (baseClass) === "object") {
prop = baseClass;
baseClass = null;
}
// The class created by this call(Constructor)
function F() {
// If you are currently in the instantiation class stage,Then callinitPrototype functions
if (!initializing) {
// If the parent class exists,The instance object'sbasePrototype pointing to parent class
// This provides a way to call the parent class method in the instance object
if (baseClass) {
= ;
}
(this, arguments);
}
}
// If this class needs to be extended from other classes
if (baseClass) {
initializing = true;
= new baseClass();
= F;
initializing = false;
}
// Override the function of the same name of the parent class
for (var name in prop) {
if ((name)) {
[name] = prop[name];
}
}
return F;
};
Call method:
 var Person = jClass({
init: function(name) {
= name;
},
getName: function() {
return ;
}
});
var Employee = jClass(Person, {
init: function(name, employeeID) {
// Call the prototype function of the parent classinit,Pay attention to useapplyFunction modificationinitofthisPoint to
(this, [name]);
= employeeID;
},
getEmployeeID: function() {
return ;
},
getName: function() {
// Call the prototype function of the parent classgetName
return "Employee name: " + (this);
}
});

var zhang = new Employee("ZhangSan", "1234");
(()); // "Employee name: ZhangSan"

 

So far, we have corrected the various disadvantages of manually implementing inheritance in Chapter 1. We use our customized jClass function to create classes and subclasses, initialize data through the prototype method init, and call the prototype function of the parent class through the instance attribute base.

The only drawback is that the code that calls the parent class is too long and difficult to understand. Wouldn't it be even better if it can be called in the following way:

 var Employee = jClass(Person, {
init: function(name, employeeID) {
// If this is possible,That's better
(name);
= employeeID;
}
});

 

Optimize jClass function

 

 // Is it currently in the stage of creating a class?
var initializing = false;
function jClass(baseClass, prop) {
// Only one parameter is accepted - jClass(prop)
if (typeof (baseClass) === "object") {
prop = baseClass;
baseClass = null;
}
// The class created by this call(Constructor)
function F() {
// If you are currently in the instantiation class stage,Then callinitPrototype functions
if (!initializing) {
// If the parent class exists,The instance object'sbaseprototypePrototype pointing to parent class
// This provides a way to call the parent class method in the instance object
if (baseClass) {
= ;
}
(this, arguments);
}
}
// If this class needs to be extended from other classes
if (baseClass) {
initializing = true;
= new baseClass();
= F;
initializing = false;
}
// Override the function of the same name of the parent class
for (var name in prop) {
if ((name)) {
// If this class inherits from the parent classbaseClassAnd there is a function of the same name in the parent class prototypename
if (baseClass &&
typeof (prop[name]) === "function" &&
typeof ([name]) === "function") {

// Redefine the functionname -
// First set the function with the same name in the function context.
// Then call the functionprop[name],Return function results

// Notice:The self-executing function here creates a context,This context returns another function,
// Variables in this context can be applied in this function,This is the closure(Closure)。
// This isJavaScriptCommon techniques used in framework development。
[name] = (function(name, fn) {
return function() {
= [name];
return (this, arguments);
};
})(name, prop[name]);

} else {
[name] = prop[name];
}
}
}
return F;
};
At this time, creating classes, subclasses and calling methods seem very elegant. Please see:
 var Person = jClass({
init: function(name) {
= name;
},
getName: function() {
return ;
}
});
var Employee = jClass(Person, {
init: function(name, employeeID) {
(name);
= employeeID;
},
getEmployeeID: function() {
return ;
},
getName: function() {
return "Employee name: " + ();
}
});

var zhang = new Employee("ZhangSan", "1234");
(()); // "Employee name: ZhangSan"

 

So far, we have created a complete function jClass to help us implement classes and inheritance in a more elegant way in JavaScript.

In future chapters, we will analyze some popular JavaScript classes and inheritance implementations on the Internet. However, no matter how change is, those implementations are nothing more than "hype" that skews the concepts mentioned in our chapter, in order to make a more elegant way of calling.