Home > Web Front-end > JS Tutorial > Detailed explanation of JavaScript inheritance (3)_js object-oriented

Detailed explanation of JavaScript inheritance (3)_js object-oriented

WBOY
Release: 2016-05-16 18:50:02
Original
746 people have browsed it

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

First let us review the examples introduced in Chapter 1:

 function Person(name) {<br>
this.name = name;<br>
}<br>
Person.prototype = {<br>
getName: function() {<br>
return this.name;<br>
}<br>
}<br><br>
function Employee(name, employeeID) {<br>
this.name = name;<br>
this.employeeID = employeeID;<br>
}<br>
Employee.prototype = new Person();<br>
Employee.prototype.getEmployeeID = function() {<br>
return this.employeeID;<br>
};<br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "ZhangSan" <br>
Copy after login

Fix constructor pointing error

From the description of the 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");<br>
console.log(zhang.constructor === Employee); // false<br>
console.log(zhang.constructor === Object); // true <br>
Copy after login
We need a simple correction:
 function Employee(name, employeeID) {<br>
this.name = name;<br>
this.employeeID = employeeID;<br>
}<br>
Employee.prototype = new Person();<br>
Employee.prototype.constructor = Employee;<br>
Employee.prototype.getEmployeeID = function() {<br>
return this.employeeID;<br>
};<br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.constructor === Employee); // true<br>
console.log(zhang.constructor === Object); // false<br>
Copy after login

It is inappropriate to instantiate Person when creating the Employee class

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

 // 空的构造函数<br>
function Person() {<br>
}<br>
Person.prototype = {<br>
init: function(name) {<br>
this.name = name;<br>
},<br>
getName: function() {<br>
return this.name;<br>
}<br>
}<br>
// 空的构造函数<br>
function Employee() {<br>
}<br>
// 创建类的阶段不会初始化父类的数据,因为Person是一个空的构造函数<br>
Employee.prototype = new Person();<br>
Employee.prototype.constructor = Employee;<br>
Employee.prototype.init = function(name, employeeID) {<br>
this.name = name;<br>
this.employeeID = employeeID;<br>
};<br>
Employee.prototype.getEmployeeID = function() {<br>
return this.employeeID;<br>
};<br>
Copy after login
In this method, the init function must be called manually after instantiating an object, as follows:
 var zhang = new Employee();<br>
zhang.init("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "ZhangSan"<br>
Copy after login

How to automatically call the init function?

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

 // 创建一个全局的状态标示 - 当前是否处于类的构造阶段<br>
var initializing = false;<br>
function Person() {<br>
if (!initializing) {<br>
this.init.apply(this, arguments);<br>
}<br>
}<br>
Person.prototype = {<br>
init: function(name) {<br>
this.name = name;<br>
},<br>
getName: function() {<br>
return this.name;<br>
}<br>
}<br>
function Employee() {<br>
if (!initializing) {<br>
this.init.apply(this, arguments);<br>
}<br>
}<br>
// 标示当前进入类的创建阶段,不会调用init函数<br>
initializing = true;<br>
Employee.prototype = new Person();<br>
Employee.prototype.constructor = Employee;<br>
initializing = false;<br>
Employee.prototype.init = function(name, employeeID) {<br>
this.name = name;<br>
this.employeeID = employeeID;<br>
};<br>
Employee.prototype.getEmployeeID = function() {<br>
return this.employeeID;<br>
};<br><br>
// 初始化类实例时,自动调用类的原型函数init,并向init中传递参数<br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "ZhangSan"<br>
Copy after login
But this requires the introduction of global variables, which is a bad sign.

How to avoid introducing global variable initializing?

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

 // 当前是否处于创建类的阶段<br>
var initializing = false;<br>
function jClass(baseClass, prop) {<br>
// 只接受一个参数的情况 - jClass(prop)<br>
if (typeof (baseClass) === "object") {<br>
prop = baseClass;<br>
baseClass = null;<br>
}<br>
// 本次调用所创建的类(构造函数)<br>
function F() {<br>
// 如果当前处于实例化类的阶段,则调用init原型函数<br>
if (!initializing) {<br>
this.init.apply(this, arguments);<br>
}<br>
}<br>
// 如果此类需要从其它类扩展<br>
if (baseClass) {<br>
initializing = true;<br>
F.prototype = new baseClass();<br>
F.prototype.constructor = F;<br>
initializing = false;<br>
}<br>
// 覆盖父类的同名函数<br>
for (var name in prop) {<br>
if (prop.hasOwnProperty(name)) {<br>
F.prototype[name] = prop[name];<br>
}<br>
}<br>
return F;<br>
};<br>
Copy after login
Methods to use jClass functions to create classes and inherit classes:
 var Person = jClass({<br>
init: function(name) {<br>
this.name = name;<br>
},<br>
getName: function() {<br>
return this.name;<br>
}<br>
});<br>
var Employee = jClass(Person, {<br>
init: function(name, employeeID) {<br>
this.name = name;<br>
this.employeeID = employeeID;<br>
},<br>
getEmployeeID: function() {<br>
return this.employeeID;<br>
}<br>
});<br><br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "ZhangSan"<br>
Copy after login
OK, now the way to create and instantiate classes looks much more elegant. But there are still obvious flaws here. Employee's initialization function init cannot call the method of the same name of the parent class.

How to call the method of the same name in the parent class?

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

 // 当前是否处于创建类的阶段<br>
var initializing = false;<br>
function jClass(baseClass, prop) {<br>
// 只接受一个参数的情况 - jClass(prop)<br>
if (typeof (baseClass) === "object") {<br>
prop = baseClass;<br>
baseClass = null;<br>
}<br>
// 本次调用所创建的类(构造函数)<br>
function F() {<br>
// 如果当前处于实例化类的阶段,则调用init原型函数<br>
if (!initializing) {<br>
// 如果父类存在,则实例对象的base指向父类的原型<br>
// 这就提供了在实例对象中调用父类方法的途径<br>
if (baseClass) {<br>
this.base = baseClass.prototype;<br>
}<br>
this.init.apply(this, arguments);<br>
}<br>
}<br>
// 如果此类需要从其它类扩展<br>
if (baseClass) {<br>
initializing = true;<br>
F.prototype = new baseClass();<br>
F.prototype.constructor = F;<br>
initializing = false;<br>
}<br>
// 覆盖父类的同名函数<br>
for (var name in prop) {<br>
if (prop.hasOwnProperty(name)) {<br>
F.prototype[name] = prop[name];<br>
}<br>
}<br>
return F;<br>
};<br>
Copy after login
Calling method:
 var Person = jClass({<br>
init: function(name) {<br>
this.name = name;<br>
},<br>
getName: function() {<br>
return this.name;<br>
}<br>
});<br>
var Employee = jClass(Person, {<br>
init: function(name, employeeID) {<br>
// 调用父类的原型函数init,注意使用apply函数修改init的this指向<br>
this.base.init.apply(this, [name]);<br>
this.employeeID = employeeID;<br>
},<br>
getEmployeeID: function() {<br>
return this.employeeID;<br>
},<br>
getName: function() {<br>
// 调用父类的原型函数getName<br>
return "Employee name: " + this.base.getName.apply(this);<br>
}<br>
});<br><br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "Employee name: ZhangSan"<br>
Copy after login

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

The only shortcoming is that the code for calling the parent class is too long and difficult to understand. Wouldn’t it be better if it could be called as follows:

 var Employee = jClass(Person, {<br>
init: function(name, employeeID) {<br>
// 如果能够这样调用,就再好不过了<br>
this.base(name);<br>
this.employeeID = employeeID;<br>
}<br>
});<br>
Copy after login

Optimize jClass function

 // 当前是否处于创建类的阶段<br>
var initializing = false;<br>
function jClass(baseClass, prop) {<br>
// 只接受一个参数的情况 - jClass(prop)<br>
if (typeof (baseClass) === "object") {<br>
prop = baseClass;<br>
baseClass = null;<br>
}<br>
// 本次调用所创建的类(构造函数)<br>
function F() {<br>
// 如果当前处于实例化类的阶段,则调用init原型函数<br>
if (!initializing) {<br>
// 如果父类存在,则实例对象的baseprototype指向父类的原型<br>
// 这就提供了在实例对象中调用父类方法的途径<br>
if (baseClass) {<br>
this.baseprototype = baseClass.prototype;<br>
}<br>
this.init.apply(this, arguments);<br>
}<br>
}<br>
// 如果此类需要从其它类扩展<br>
if (baseClass) {<br>
initializing = true;<br>
F.prototype = new baseClass();<br>
F.prototype.constructor = F;<br>
initializing = false;<br>
}<br>
// 覆盖父类的同名函数<br>
for (var name in prop) {<br>
if (prop.hasOwnProperty(name)) {<br>
// 如果此类继承自父类baseClass并且父类原型中存在同名函数name<br>
if (baseClass &&<br>
typeof (prop[name]) === "function" &&<br>
typeof (F.prototype[name]) === "function") {<br><br>
// 重定义函数name - <br>
// 首先在函数上下文设置this.base指向父类原型中的同名函数<br>
// 然后调用函数prop[name],返回函数结果<br><br>
// 注意:这里的自执行函数创建了一个上下文,这个上下文返回另一个函数,<br>
// 此函数中可以应用此上下文中的变量,这就是闭包(Closure)。<br>
// 这是JavaScript框架开发中常用的技巧。<br>
F.prototype[name] = (function(name, fn) {<br>
return function() {<br>
this.base = baseClass.prototype[name];<br>
return fn.apply(this, arguments);<br>
};<br>
})(name, prop[name]);<br><br>
} else {<br>
F.prototype[name] = prop[name];<br>
}<br>
}<br>
}<br>
return F;<br>
};<br>
Copy after login
At this point, creating classes and subclasses and calling methods are very elegant, please see:
 var Person = jClass({<br>
init: function(name) {<br>
this.name = name;<br>
},<br>
getName: function() {<br>
return this.name;<br>
}<br>
});<br>
var Employee = jClass(Person, {<br>
init: function(name, employeeID) {<br>
this.base(name);<br>
this.employeeID = employeeID;<br>
},<br>
getEmployeeID: function() {<br>
return this.employeeID;<br>
},<br>
getName: function() {<br>
return "Employee name: " + this.base();<br>
}<br>
});<br><br>
var zhang = new Employee("ZhangSan", "1234");<br>
console.log(zhang.getName()); // "Employee name: ZhangSan"<br>
Copy after login

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

In the following chapters, we will successively analyze the implementation of some of the more popular JavaScript classes and inheritance on the Internet. However, everything remains the same, and those implementations are nothing more than "hype" that shakes up the concepts we mentioned in this chapter, just for the sake of a more elegant way of calling.

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template