JavaScript面向对象的继承机制——类式继承
前言
昨天参加了公司的《js面向对象的组件》的培训,回来后有所感悟,结合自己以前学到的以及感悟的,今天在这里写出来,供大家分享。
在javascript中继承是一个复杂的话题,相对于其他面向对象的语言中的继承要复杂的多,在其他面向对象的语言中,继承一个类只需要使用一个关键字即可,比如java,只需要extended关键字即可实现继承,但在javascript中要达到继承的目的,需要进行一些额外的处理。JavaScript是属于使用原型式继承的少数语言之一,得益于JavaScript语言的灵活性,我们既可以使用标准的基于类继承,也可以使用原型式继承。由于时间有限,今天在这里先说说类式继承!原型继承等下次有时间再写。
类式继承
Javascript可以装扮撑使用类式继承的语言。如下例是一个Person类:
function Person(name){
this.name=name;
}
Person.prototype.getName=function(){
return this.name;
};
要创建Person类的一个实例,只需编写如下代码即可:
var mark=new Person("mark");
mark.getName();
但是要创建一个继承Person的类则要复杂一些:
function Man(name,wife){
Person.call(this,name);
this.wife=wife;
}
Man.prototype=new Person();
Man.prototype.constructor=Man;
Man.prototype.getWife=function(){
return this.wife;
};
代码中使用Man.prototype=new Person();Man.prototype.constructor=Man;
Person.call(this,name)语句实现Man继承Person类,其中Man.prototype=new Person();实现Man继承Person类的一个实例对象,Man.prototype.constructor=Man;用于确保Man的构造函数被正确设置,Person.call(this,name)用于确保调用Person的公有成员时的指定其执行环境为Man实例对象,而不是Person的实例对象。
让一个类继承另一个类需要很多行代码,而不像在java语言中只需要用一个extend即可。首先要做的就是像前面示例中那样创建一个构造函数,并在构造函数中调用超类的构造函数,并将参数传递给超类的构造函数。
然后是设置原型链。尽管相关代码比较简单,但是实际上这还是一个比较复杂的话题。在JavaScript中每个函数都有一个名为prototype的属性,这个属性要么指向另一个对象,要么为null。在访问对象的某个成员时,如果在当前对象中找不到该成员,那么JavaScript会沿着当前对象的prototype属性,去该属性指向的对象中查找该成员,找到就返回该成员,如果还是找不到,则继续沿着该属性指向的对象的prototype属性去查找该成员,直到找到或者已经查到原型链的最顶端的Object.prototype对象。这意味这让一个类继承另一个类,只需将子类的prototype设置为指向超类的一个实例对象即可。这与其他语言中的继承机制迥然不同。
为了让Man继承Person,必须手工将Man的prototype设置为Person类的一个实例,最后一步是将prototype的constructor属性重新设置为Man,因为把prototype属性设置为Person实例时,其constructor属性被抹除了(定义一个构造函数时,其默认的prototype属性指向的是一个空的对象,而且其constructor属性会被自动的设置为该构造函数本身,如果手工将其prototype属性设置为另一个对象,那么新对象自然不会具有原对象的constructor值,需要重新纠正其值)。
创建Man的实例对象的代码如下:
mark=new Man("mark","XXXX");
mark.getName();
mark.getWife();
由此可见类式继承的所有复杂性只限于类的声明,创建新实例的过程仍然很简单。为了简化类的声明,可以把派生子类的整个过程包装在一个extended函数中,代码如下:
function extend(subClass, superClass){
var F = function(){};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;//确保子类的构造函数被正确设置。
}
这个函数与我们之前手工做的几乎一样,它设置了prototype,然后重新设置了constructor值,作为一项改进,它添加了一个空函数F,并用它创建了一个实例对象插入原型链中,这样做可避免创建超类的新实例,因为超类的构造函数可能会进行一些额外的大量的计算任务。
使用extended函数后,前面的例子变为:
/**
* Person类
* @param {Object} name
*/
function Person(name){
this.name = name;
}
Person.prototype.getName = function(){
return this.name;
};
/**
* Man类
* @param {Object} name
* @param {Object} wife
*/
function Man(name, wife){
Person.call(this, name);
this.wife = wife;
}
extend(Man,Person);
Man.prototype.getWife = function(){
return this.wife;
};
该例子不需要再像前面一样手工个设置prototype与constructor属性,通过在类声明之后立即调用该方法(在向prototype添加任何属性之前,这个顺序很重要),唯一的问题是超类(Person)的名字被固定在了Man里。更好的做法是像下面这样来引用父类:
function extend(subClass, superClass){
var F = function(){};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;//确保子类的构造函数被正确设置。
subClass.superclass = superClass.prototype;
if (superClass.prototype.constructor == Object.prototype.constructor) {
superClass.prototype.constructor = superClass;//确保父类构造函数正确设置。
}
}
相对与第一个版本的extended函数,它多了最后3行代码,这几行代码的主要作用是确保超类的constructor属性被正确设置,在用这个新的superClass属性调用超类的构造函数时这个问题很重要:
/**
* Man类
* @param {Object} name
* @param {Object} wife
*/
function Man(name, wife){
Man.superclass.constructor.call(this,name);//这里必须确保父类的constructor属性得到正确设置。
this.wife = wife;
}
extend(Man, Person);
Man.prototype.getWife = function(){
return this.wife;
};
有了superclass属性,就可以直接诶调用超类中的方法,这在想重新定义超类的某个方法而又想访问超类的实现时可以派上用场,例如上面的例子中,想在Man中重新定义Person类中的同名方法时,可以这么做:
Man.prototype.getName=function(){
var name=Man.superclass.getName.call(this);
return name+",Hello World!";
}
结语
JavaScript类式继承的实现并不复杂,但其原理却不简单,需要对原型链有一定的理解,并对函数的执行环境有所理解。由于时间所限,这里并不多做解说,有兴趣的兄弟可以自己查找并参考相关资料。
相关推荐
JavaScript,作为一种广泛应用...JavaScript 面向对象程序设计——继承与多态.pdf 和 JavaScript 面向对象程序设计——封装.pdf 这两个文档可能深入探讨了这些主题,帮助读者深入理解并掌握JavaScript的面向对象开发。
ES6引入了更现代的面向对象语法——类,它使得面向对象编程更加直观和简洁。 **示例代码**: ```javascript class Vehicle { constructor(make, model, year) { this.make = make; this.model = model; this....
"JavaScript面向对象特性浅析与范例" JavaScript是一种基于原型的解释型语言,它允许在任意时刻给一个对象添加任意多的属性和方法。 JavaScript的面向对象特性是指它可以实现面向对象编程的部分特性,而不是完全...
此外,教程还会涉及JavaScript的面向对象编程,包括构造函数、原型链、继承和封装。这些概念对于构建大型、复杂的Web应用至关重要。 ES6(ECMAScript 2015)是JavaScript的一个重要版本,引入了许多新特性,如箭头...
这两本书——"JavaScript 面向对象编程.pdf"和"JavaScript 设计模式与开发实践.pdf"将深入探讨这些主题。 面向对象编程在JavaScript中主要体现在三个方面:封装、继承和多态。封装是指将数据和操作数据的方法捆绑在...
JavaScript是一种广泛应用于Web开发的脚本语言,尤其在构建交互式网页和动态应用程序...通过阅读《JavaScript面向对象技术整理.docx》和参考《javascript中文经典帮助手册.chm》,你可以更深入地学习和掌握这些知识点。
JavaScript是一种广泛应用于Web开发的动态编程语言,尤其在构建交互式和富媒体网站时不可或缺。...《JavaScript面向对象编程指南》这本书会是深入这一主题的好资源,建议仔细阅读并实践其中的示例。
JavaScript面向对象技术在实现树形控件中扮演着至关重要的角色。树形控件是一种用于展示层次型数据的用户界面元素,它具有扩展和折叠功能,可以在有限的空间内清晰地展示大量的信息,使得数据间的层级关系一目了然。...
面向对象编程(Object-Oriented Programming, OOP)是一种编程范式,它将数据和处理数据的方法绑定在一起,形成一个整体——对象。OOP 的主要特征包括封装、继承和多态。 1. **封装**:封装是将数据和操作数据的...
### JavaScript中的继承——类继承 #### 一、引言 JavaScript是一种动态的、弱类型的编程语言,它支持面向对象编程模式。与传统的面向对象语言如Java或C#不同,JavaScript的继承模型基于原型,这使得它的继承机制...
本文实例为大家介绍了javascript实现继承的6种方式,分享给大家供大家参考,具体内容如下 1、【原型链继承】实现的本质是重写原型对象,代之以一个新类型的实例。实际上不是SubType的原型的constructor属性被重写了...
### JavaScript面向对象继承详解 #### 一、引言 JavaScript是一种多范式的编程语言,它支持面向对象编程(OOP)和其他多种编程风格。在面向对象编程中,“继承”是一项非常重要的特性,允许子类继承父类的属性和...
### 浅谈JavaScript面向对象编程 #### 一、概述 面向对象编程(OOP)是一种编程范式,它使用“对象”来设计软件。在JavaScript中,尽管语言本身并不支持传统的类(class)概念,但开发者可以通过原型继承和其他技术来...
JavaScript作为一种动态语言,其对象模型与传统的面向对象编程语言有所不同。在JavaScript中,并没有直接提供类的概念,而是通过原型来实现继承。本文将深入探讨JavaScript中的原型继承机制,帮助读者理解其中的关键...
JavaScript,作为一种广泛使用的脚本语言,常常被误解为非面向对象的语言,但实际上,JavaScript完全支持面向对象编程(OOP)的三大核心原则:封装、继承和多态。尽管它的实现方式与传统的面向对象语言(如Java或C++...
JavaScript是面向对象的编程语言,但其继承机制与传统的类继承有所不同。在JavaScript中,对象是基于原型(prototype)的,而不是基于类。本篇主要探讨两种非经典的继承实现方式:zInherit和xbObjects。 1. **...
JavaScript中的继承机制是其面向对象特性的重要组成部分。虽然JavaScript并非完全传统的面向对象语言,如Java或C#,但它的继承方式依然提供了实现面向对象编程的能力。JavaScript的继承主要通过原型链(prototype ...