JavaScript inheritance (III)

Source: Internet
Author: User
Tags hasownproperty

Note: The implementation of jClass in this chapter refers to the practice of Simple JavaScript Inheritance.

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

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

function Employee(name, employeeID) {
this.name = name;
this.employeeID = employeeID;
}
Employee.prototype = new Person();
Employee.prototype.getEmployeeID = function() {
return this.employeeID;
};
var zhang = new Employee("ZhangSan", "1234");
console.log(zhang.getName()); // "ZhangSan"

 

Corrected the constructor pointing error.

 

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

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

 

It is inappropriate to instantiate Person when creating an Employee class.

 

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

// Empty Constructor
Function Person (){
}
Person. prototype = {
Init: function (name ){
This. name = name;
},
GetName: function (){
Return this. name;
}
}
// Empty Constructor
Function Employee (){
}
// The data of the parent class will not be initialized during class creation, because Person is an empty constructor.
Employee. prototype = new Person ();
Employee. prototype. constructor = Employee;
Employee. prototype. init = function (name, employeeID ){
This. name = name;
This. employeeID = employeeID;
};
Employee. prototype. getEmployeeID = function (){
Return this. employeeID;
};
In this way, you must manually call the init function after instantiating an object, as shown below:
 var zhang = new Employee();
zhang.init("ZhangSan", "1234");
console.log(zhang.getName()); // "ZhangSan"

 

How to automatically call the init function?

 

There must be two effects. When constructing a class, do not call the init function or automatically call the init function when instantiating an object. It seems that we need to have a status sign when calling an empty constructor.

// Create a global status flag-whether or not it is in the Class Construction Stage
Var initializing = false;
Function Person (){
If (! Initializing ){
This. init. apply (this, arguments );
}
}
Person. prototype = {
Init: function (name ){
This. name = name;
},
GetName: function (){
Return this. name;
}
}
Function Employee (){
If (! Initializing ){
This. init. apply (this, arguments );
}
}
// Indicates that the current class is being created and the init function is not called.
Initializing = true;
Employee. prototype = new Person ();
Employee. prototype. constructor = Employee;
Initializing = false;
Employee. prototype. init = function (name, employeeID ){
This. name = name;
This. employeeID = employeeID;
};
Employee. prototype. getEmployeeID = function (){
Return this. employeeID;
};

// When initializing a class instance, the class's prototype function init is automatically called and parameters are passed to the init.
Var zhang = new Employee ("ZhangSan", "1234 ");
Console. log (zhang. getName (); // "ZhangSan"
However, global variables must be introduced, which is a bad signal.

 

How to avoid introducing the global variable initializing?

 

We need to introduce a global function to simplify the class creation process and encapsulate internal details to avoid introducing global variables.

// Whether it is in the stage of creating a class
Var initializing = false;
Function jClass (baseClass, prop ){
// When only one parameter is accepted-jClass (prop)
If (typeof (baseClass) = "object "){
Prop = baseClass;
BaseClass = null;
}
// The class (constructor) created in this call)
Function F (){
// If the current stage is in the instantiation class stage, the init prototype function is called.
If (! Initializing ){
This. init. apply (this, arguments );
}
}
// If this class needs to be extended from other classes
If (baseClass ){
Initializing = true;
F. prototype = new baseClass ();
F. prototype. constructor = F;
Initializing = false;
}
// Override the Same Name function of the parent class
For (var name in prop ){
If (prop. hasOwnProperty (name )){
F. prototype [name] = prop [name];
}
}
Return F;
};
Use the jClass function to create classes and inherit classes:
 var Person = jClass({
init: function(name) {
this.name = name;
},
getName: function() {
return this.name;
}
});
var Employee = jClass(Person, {
init: function(name, employeeID) {
this.name = name;
this.employeeID = employeeID;
},
getEmployeeID: function() {
return this.employeeID;
}
});

var zhang = new Employee("ZhangSan", "1234");
console.log(zhang.getName()); // "ZhangSan"
OK. Now the method for creating and instantiating classes looks much more elegant. However, there are still obvious flaws. The initialization function init of the Employee cannot call the method of the same name of the parent class.

 

How do I call methods with the same name as the parent class?

 

We can provide a base attribute for the instantiated object to point to the prototype of the parent class (constructor), as follows:

// Whether it is in the stage of creating a class
Var initializing = false;
Function jClass (baseClass, prop ){
// When only one parameter is accepted-jClass (prop)
If (typeof (baseClass) = "object "){
Prop = baseClass;
BaseClass = null;
}
// The class (constructor) created in this call)
Function F (){
// If the current stage is in the instantiation class stage, the init prototype function is called.
If (! Initializing ){
// If the parent class exists, the base of the Instance Object points to the prototype of the parent class.
// This provides a way to call the parent class method in the instance object.
If (baseClass ){
This. base = baseClass. prototype;
}
This. init. apply (this, arguments );
}
}
// If this class needs to be extended from other classes
If (baseClass ){
Initializing = true;
F. prototype = new baseClass ();
F. prototype. constructor = F;
Initializing = false;
}
// Override the Same Name function of the parent class
For (var name in prop ){
If (prop. hasOwnProperty (name )){
F. prototype [name] = prop [name];
}
}
Return F;
};
Call method:
Var Person = jClass ({
Init: function (name ){
This. name = name;
},
GetName: function (){
Return this. name;
}
});
Var Employee = jClass (Person ,{
Init: function (name, employeeID ){
// Call the parent class's prototype function init. Use the apply function to modify the this point of init.
This. base. init. apply (this, [name]);
This. employeeID = employeeID;
},
GetEmployeeID: function (){
Return this. employeeID;
},
GetName: function (){
// Call the prototype function getName of the parent class
Return "Employee name:" + this. base. getName. apply (this );
}
});

Var zhang = new Employee ("ZhangSan", "1234 ");
Console. log (zhang. getName (); // "Employee name: ZhangSan"

 

So far, we have corrected the drawbacks of manual inheritance in Chapter 1. Use our custom jClass function to create classes and subclasses, use the prototype method init to initialize data, and call the prototype function of the parent class through the instance attribute base.

The only defect is that the Code for calling the parent class is too long and hard to understand. It would be better if the code can be called in the following way:

Var Employee = jClass (Person ,{
Init: function (name, employeeID ){
// If it can be called like this, it would be better.
This. base (name );
This. employeeID = employeeID;
}
});

 

Optimize jClass Functions

 

// Whether it is in the stage of creating a class
Var initializing = false;
Function jClass (baseClass, prop ){
// When only one parameter is accepted-jClass (prop)
If (typeof (baseClass) = "object "){
Prop = baseClass;
BaseClass = null;
}
// The class (constructor) created in this call)
Function F (){
// If the current stage is in the instantiation class stage, the init prototype function is called.
If (! Initializing ){
// If the parent class exists, the baseprototype of the Instance Object points to the prototype of the parent class.
// This provides a way to call the parent class method in the instance object.
If (baseClass ){
This. baseprototype = baseClass. prototype;
}
This. init. apply (this, arguments );
}
}
// If this class needs to be extended from other classes
If (baseClass ){
Initializing = true;
F. prototype = new baseClass ();
F. prototype. constructor = F;
Initializing = false;
}
// Override the Same Name function of the parent class
For (var name in prop ){
If (prop. hasOwnProperty (name )){
// If the class inherits from the parent class baseClass and the parent class has a function with the same name in the prototype
If (baseClass &&
Typeof (prop [name]) === "function "&&
Typeof (F. prototype [name]) = "function "){

// Redefinition function name-
// First, set this. base in the function context to point to the same name function in the parent class prototype.
// Then call the function prop [name] and return the function result.

// Note: The self-executed function creates a context, which returns another function,
// This function can apply the variables in this context. This is the Closure (Closure ).
// This is a common technique in JavaScript framework development.
F. prototype [name] = (function (name, fn ){
Return function (){
This. base = baseClass. prototype [name];
Return fn. apply (this, arguments );
};
}) (Name, prop [name]);

} Else {
F. prototype [name] = prop [name];
}
}
}
Return F;
};
In this case, creating classes, subclasses, and calling methods are very elegant. See:
 var Person = jClass({
init: function(name) {
this.name = name;
},
getName: function() {
return this.name;
}
});
var Employee = jClass(Person, {
init: function(name, employeeID) {
this.base(name);
this.employeeID = employeeID;
},
getEmployeeID: function() {
return this.employeeID;
},
getName: function() {
return "Employee name: " + this.base();
}
});

var zhang = new Employee("ZhangSan", "1234");
console.log(zhang.getName()); // "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 subsequent chapters, we will analyze some popular JavaScript classes and inheritance implementations on the Internet. However, the implementation is nothing more than the "hype" that we mentioned in this Chapter, in order to be a more elegant call method.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.