JavaScript inheritance (3) and javascript inheritance

Source: Internet
Author: User
Tags hasownproperty

JavaScript inheritance (3) and javascript inheritance

In chapter 1, we use constructors and prototypes to implement classes and inheritance in the JavaScript world, but there are many problems. In this chapter, we will analyze these problems one by one and provide solutions.

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); // falseconsole.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); // trueconsole.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 ;}/// the 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 the current status is in the class construction phase 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 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 the parameter var zhang = new Employee ("ZhangSan", "1234") is passed to the init "); 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 the current stage is in the class creation phase var initializing = false; function jClass (baseClass, prop) {// only accept one parameter-jClass (prop) if (typeof (baseClass) = "object") {prop = baseClass; baseClass = null;} // The class (constructor) function F () created in this call () {// if the current stage is in the instantiation class stage, call the init prototype function 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;} // overwrite 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 the current stage is in the class creation phase var initializing = false; function jClass (baseClass, prop) {// only accept one parameter-jClass (prop) if (typeof (baseClass) = "object") {prop = baseClass; baseClass = null;} // The class (constructor) function F () created in this call () {// if the current stage is in the instantiation class stage, call the init prototype function 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;} // overwrite 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 this of init to point to this. base. init. apply (this, [name]); this. employeeID = employeeID;}, getEmployeeID: function () {return this. employeeID;}, getName: function () {// call the parent class's prototype function getNamereturn "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 the current stage is in the class creation phase var initializing = false; function jClass (baseClass, prop) {// only accept one parameter-jClass (prop) if (typeof (baseClass) = "object") {prop = baseClass; baseClass = null;} // The class (constructor) function F () created in this call () {// if the current stage is in the instantiation class stage, call the init prototype function 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;} // overwrite the Same name function of the parent class for (var name in prop) {if (prop. hasOwnProperty (name) {// if this class inherits from the parent class baseClass and the parent class has a function with the same name n Ameif (baseClass & typeof (prop [name]) === "function" & typeof (F. prototype [name]) = "function") {// redefine the function name-// first set this in the function context. base points to the function with the same name in the parent class prototype // then calls the function prop [name] and returns the function result. // Note: The self-executed function creates a context, this context 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.

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.