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");We need a simple correction:
( === Employee); // false
( === Object); // true
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 constructorIn this way, the init function must be manually called after instantiating an object, as follows:
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 ;
};
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?But in this way, global variables must be introduced, which is a bad signal.
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"
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?Use jClass function to create classes and inherit classes:
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;
};
var Person = jClass({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.
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"
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?Call method:
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;
};
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?At this time, creating classes, subclasses and calling methods seem very elegant. Please see:
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;
};
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.