In the first chapter, we use constructors and prototypes to implement classes and inheritance in the world of JavaScript, but there are many problems. In this chapter we will analyze each of these issues and give a solution.
Note: The implementation of Jclass in this chapter refers to the simple JavaScript inheritance approach.
First, let's review the example presented in the first chapter:
Fixed pointing error for constructor
From the description of constructor in the previous article, we know that the constructor of the employee instance will have a pointing error, as follows:
We need a simple fix:
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 not appropriate to instantiate a person when creating an employee class
But on the other hand, we have to rely on this mechanism to implement inheritance. The workaround is not to initialize the data in the constructor, but rather to provide a prototype method (such as Init) to initialize the data.
Null constructor function person () {}person.prototype = {init:function (name) {this.name = Name;},getname:function () {return this . Name;}} Empty constructor function Employee () {}//The stage of creating a class does not initialize the data of the parent class, because the 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 invoke the INIT function after instantiating an object, as follows:
var Zhang = new Employee (), Zhang.init ("Zhangsan", "1234"); Console.log (Zhang.getname ()); "Zhangsan"
How do I call the init function automatically?
There must be two effects, and the Init function is called automatically when the class is constructed without calling the Init function and instantiating the object. It seems that we need to have a status indicator when we call an empty constructor.
Creates a global status indicator-is currently in the construction phase of the class var initializing = False;function person () {if (!initializing) {this.init.apply (this, argume NTS);}} 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 init function initializing = True is not called when the creation phase of the class is currently entered. 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;};/ /Initializes the class instance, automatically invokes the class's prototype function init, and passes the parameters to init var Zhang = new Employee ("Zhangsan", "1234"); Console.log (Zhang.getname ()); "Zhangsan"
But it's a bad sign that you have to introduce global variables. How to avoid introducing global variable initializing?
We need to introduce a global function to simplify the process of creating classes, while encapsulating internal details to avoid introducing global variables.
is currently in the stage of creating the class var initializing = False;function Jclass (baseclass, prop) {//Accept only one parameter case-Jclass (prop) if (typeof (Baseclas s) = = = "Object") {prop = Baseclass;baseclass = null;} This call creates a class (constructor) function F () {//If you are currently in the stage of instantiating a class, 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;} Overrides the parent class with the same name function for (var name in prop) {if (Prop.hasownproperty (name)) {F.prototype[name] = Prop[name];}} return F;};
Use the Jclass function to create a method for classes and inheriting 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;},getemplo Yeeid:function () {return this.employeeid;}}); var Zhang = new Employee ("Zhangsan", "1234"); Console.log (Zhang.getname ()); "Zhangsan"
OK, now the way to create classes and instantiate classes looks much more elegant. However, there are obvious flaws in this, and employee initialization function Init cannot call the parent class with the same name method. How do I call a method of the parent class with the same name?
We can point to the prototype of the parent class (constructor) by providing a base property for the instantiated object, as follows:
is currently in the stage of creating the class var initializing = False;function Jclass (baseclass, prop) {//Accept only one parameter case-Jclass (prop) if (typeof (Baseclas s) = = = "Object") {prop = Baseclass;baseclass = null;} This call creates a class (constructor) function F () {//If the stage of the instantiated class is currently in, 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 an 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;} Overrides the parent class with the same name function for (var name in prop) {if (Prop.hasownproperty (name)) {F.prototype[name] = Prop[name];}} return F;};
Call Mode:
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, Note Use the Apply function to modify the 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 fixed all the drawbacks of manually implementing inheritance in Chapter I. Using our custom Jclass function to create classes and subclasses, initialize the data through the prototype method Init, and invoke the prototype function of the parent class through the instance property base.
The only drawback is that the code calling the parent class is too long and doesn't understand well, but it would be better if it could be called as follows:
var Employee = Jclass (person, {init:function (name, EmployeeID) {//) if it can be called, it will be better than this.base (name); This.employeeid = EmployeeID;}});
Optimizing Jclass Functions
is currently in the stage of creating the class var initializing = False;function Jclass (baseclass, prop) {//Accept only one parameter case-Jclass (prop) if (typeof (Baseclas s) = = = "Object") {prop = Baseclass;baseclass = null;} This call creates a class (constructor) function F () {//If the stage of the instantiated class is currently in, 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 an 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;} Overrides the parent class with the same name function for (var name in prop) {if (Prop.hasownproperty (name)) {//) If this class inherits from the parent class BaseClass and there is a function with the same name in the parent prototype Nameif (BaseClass & Amp;&typeof (prop[name]) = = = "function" &&typeof (f.prototype[name]) = = = "function") {//Redefine function name-//First on function The following setting this.base points to the same name function in the parent prototype//and then calls the function Prop[name], returns the function result//NOTE: The self-executing function here creates a context that returns another function,//The variable in this context can be applied in this function. This is the closure (Closure). This is a common technique used 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;};
At this point, the creation of classes and subclasses, and the way they are invoked, looks 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've created a perfect function, Jclass, to help us implement classes and inheritance in JavaScript in a more elegant way.
In later chapters, we will analyze some of the more popular JavaScript classes and inheritance implementations on the web. But original aim, those implementations have nothing but the "hype" that winnow the concepts mentioned in our chapter, which is a more elegant way of calling.
JavaScript inheritance in detail (iii)