javascript 面向对象编程基础:继承


Posted in Javascript onAugust 21, 2009

我们看到这里继承的概念是多么的直白,“拷贝一个类的prototype 到另外一个类”,好,Code is cheap,看代码:

function class1() { }
function class2() { }
class2.prototype = class1.prototype;
class2.moreProperty1 = " class 2 additional string " ;
class2.moreMethod1 = function () { alert( " class 2 additional method " ); }
/*
这样,首先是class2具有了和class1 一样的prototype,不考虑构造函数,两个类是等价的。
随后,又通过prototype给class2赋予了两个额外的方法。所以class2是在class1的基础上
增加了属性和方法,这就实现了类的继承。
*/

function test() {
var obj = new class2();
// JavaScript提供了instanceof 操作符来判断一个对象是否是某个类的实例
alert(obj instanceof class2); // true
alert(obj instanceof class1); // ?
}

运行代码,结果是不是在我们的意料之中?表面上看,上面的实现完全可行,js也可以正确的理解和实现这种继承关系,obj同时是class1和 class2的实例,但实质上则不然(我们学习的目的是要知其然更要知其所以然)。js的这种理解实际上是基于一种很简单的策略,看下面的代码,先使用 prototype让class2 继承于class1,再在class2 中重复定义method 方法:

// 定义class1
function class1() {
// 构造函数
}
// 定义class1 的成员
class1.prototype = {
m1: function () { // 方法1
alert( " class1 method1 " );
}
}
// 定义class2
function class2() {
// 构造函数
}
// 让class2 继承于class1
class2.prototype = class1.prototype;

// 给class2 重复定义方法method
class2.prototype.method = function () {
alert( " whose method2? class1 or class2 " );
}
// 创建两个类的实例
var obj1 = new class1();
var obj2 = new class2();

function test() {
// 分别调用两个对象的method 方法
obj1.method();
obj2.method();
}

从代码执行结果看,method方法在class1,2中运行的结果是相同的。

由此可见,当对class2 进行prototype 的改变时,class1的prototype也随之改变,即使对class2 的prototype 增减一些成员,class1的成员也随之改变。所以class1 和class2 仅仅是构造函数不同的两个类,它们保持着相同的成员定义。说到这里,相信读者已经发现了其中的奥妙:class1 和class2 的prototype 是完全相同的,是对同一个对象的引用。其实从这条赋值语句就可以看出来:
//让class2 继承于class1
class2.prototype=class1.prototype;
在js中,除了基本的数据类型(数字、字符串、布尔类型等),所有的赋值以及函数参数都是引用传递,而不是值传递。所以上面的语句仅仅是让class2 的prototype 对象引用class1 的prototype,造成了类成员定义始终保持一致的效果。从这里也看到了instanceof 操作符的执行机制,它就是判断一个对象是否是一个prototype 的实例,因为这里的obj1 和obj2 都是对应于同一个prototype,所以它们instanceof 的结果都是相同的。由此可见,使用prototype 引用拷贝实现继承不是一种正确的办法。但在要求不严格的情况下,却也是一种合理的方法,唯一的约束是不允许类成员的覆盖定义(这里其实也是js的灵活性的体现)。其实,我们完全可以利用反射机制和prototype 来实现js正确的类继承:

function class1() {
// 构造函数
}
class1.prototype = {
method: function () {
alert( " method1 " );
},
method2: function () {
alert( " method2 " );
}
}
function class2() {
// 构造函数
}

// 让class2 继承于class1
for ( var p in class1.prototype) {
class2.prototype[p] = class1.prototype[p]; // 利用反射机制和prototype实现继承
}

// 覆盖定义class1中的method 方法
class2.prototype.method = function () {
alert( " class2 new method1 " );
}

// 创建两个类的实例
var obj1 = new class1();
var obj2 = new class2();

function test() {
// 分别调用两个对象的method 方法
obj1.method();
obj2.method();
// 分别调用两个对象的method2 方法
obj1.method2();
obj2.method2();
}

从运行结果可见,obj2中重复定义的method 已经覆盖了继承的method方法,同时method2 方法未受影响。而且obj1中的method 方法仍然保持了原有的定义。这样,就实现了正确意义的类的继承。为了方便开发,可以为每个类添加一个共有的方法,用以实现类的继承:

// 为类添加静态方法inherit表示继承于某类
Function.prototype.inherit = function (baseClass) {
for ( var p in baseClass.prototype) {
this .prototype[p] = baseClass.prototype[p];
}
}

function class1() {
// 构造函数
}
class1.prototype = {
method: function () {
alert( " method1 " );
},
method2: function () {
alert( " method2 " );
}
}
function class2() {
// 构造函数
}

// 让class2 继承于class1
// for (var p in class1.prototype) {
// class2.prototype[p] = class1.prototype[p]; // 利用反射机制和prototype实现继承
// }

class2.inherit(class1); // 等价于上面注释掉的那一个for循环

// 覆盖定义class1中的method 方法
class2.prototype.method = function () {
alert( " class2 new method1 " );
}

// 创建两个类的实例
var obj1 = new class1();
var obj2 = new class2();

function test() {
// 分别调用两个对象的method 方法
obj1.method();
obj2.method();
// 分别调用两个对象的method2 方法
obj1.method2();
obj2.method2();
}

上面的代码使逻辑变的更加清楚,也更容易理解。通过这种方法实现的继承,有一个缺点,就是在class2 中添加类成员定义时,不能给prototype 直接赋值,而只能对其属性进行赋值,例如不能为:
class2.prototype={
//成员定义
}
而只能为:
class2.prototype.propertyName=someValue;
class2.prototype.methodName=function(){
//语句
}

由此可见,这样实现继承仍然要以牺牲一定的代码可读性为代价。有没有“不仅基类可以用对象直接赋值给property,而且在派生类中也可以同样实现,使代码逻辑更加清晰,也更能体现面向对象的语言特点”的js继承方式?引号里的说法是多么的诱人啊,继续学习去了。

Javascript 相关文章推荐
Prototype 学习 Prototype对象
Jul 12 Javascript
JS复制内容到剪切板的实例代码(兼容IE与火狐)
Nov 19 Javascript
javascript 自定义回调函数示例代码
Sep 26 Javascript
详谈JavaScript 匿名函数及闭包
Nov 14 Javascript
Javascript访问器属性实例分析
Dec 30 Javascript
js接收并转化Java中的数组对象的方法
Aug 11 Javascript
AngularJS监听路由的变化示例代码
Sep 23 Javascript
最实用的JS数组函数整理
Dec 05 Javascript
js循环map 获取所有的key和value的实现代码(json)
May 09 Javascript
vue+axios 前端实现登录拦截的两种方式(路由拦截、http拦截)
Oct 24 Javascript
vue拖拽组件 vuedraggable API options实现盒子之间相互拖拽排序
Jul 08 Javascript
layui复选框的全选与取消实现方法
Sep 02 Javascript
javascript 面向对象编程基础:封装
Aug 21 #Javascript
javascript arguments 传递给函数的隐含参数
Aug 21 #Javascript
javascript 自定义事件初探
Aug 21 #Javascript
IE 下的只读 innerHTML
Aug 21 #Javascript
JS 控制CSS样式表
Aug 20 #Javascript
JS获取父节点方法
Aug 20 #Javascript
javascript 数组排序函数
Aug 20 #Javascript
You might like
PHP表单提交表单名称含有点号(.)则会被转化为下划线(_)
2011/12/14 PHP
thinkphp路由规则使用示例详解和伪静态功能实现(apache重写)
2014/02/24 PHP
ThinkPHP的模版中调用session数据的方法
2014/07/01 PHP
Laravel中Facade的加载过程与原理详解
2017/09/22 PHP
JavaScript DOM 学习第五章 表单简介
2010/02/19 Javascript
在Javascript里访问SharePoint列表数据的实现方法
2011/05/22 Javascript
基于JavaScript 声明全局变量的三种方式详解
2013/05/07 Javascript
快速解决FusionCharts联动的中文乱码问题
2013/12/04 Javascript
jquery遍历筛选数组的几种方法和遍历解析json对象
2013/12/13 Javascript
JS基于ocanvas插件实现的简单画板效果代码(附demo源码下载)
2016/04/05 Javascript
jQuery中DOM节点的删除方法总结(超全面)
2017/01/22 Javascript
JavaScript中undefined和null的区别
2017/05/03 Javascript
AngularJS页面带参跳转及参数解析操作示例
2017/06/28 Javascript
详解weex默认webpack.config.js改造
2018/01/08 Javascript
Vuex实现计数器以及列表展示效果
2018/03/10 Javascript
p5.js入门教程之小球动画示例代码
2018/03/15 Javascript
React Native悬浮按钮组件的示例代码
2018/04/05 Javascript
VUE实现密码验证与提示功能
2019/10/18 Javascript
JavaScript 如何在浏览器中使用摄像头
2020/12/02 Javascript
pycharm 使用心得(六)进行简单的数据库管理
2014/06/06 Python
python统计字符串中指定字符出现次数的方法
2015/04/04 Python
Python中的hypot()方法使用简介
2015/05/18 Python
python bottle框架支持jquery ajax的RESTful风格的PUT和DELETE方法
2017/05/24 Python
python增加矩阵维度的实例讲解
2018/04/04 Python
HTML5+lufylegend实现游戏中的卷轴
2016/02/29 HTML / CSS
New Era英国官网:美国棒球帽品牌
2018/03/21 全球购物
诗普兰迪官方网站:Splendid
2018/09/18 全球购物
学校采购员岗位职责
2014/01/02 职场文书
大学生毕业鉴定
2014/01/31 职场文书
《乌鸦和狐狸》教学反思
2014/02/08 职场文书
毕业自我鉴定书
2014/03/24 职场文书
2014年五一劳动节社区活动总结
2014/04/14 职场文书
市场开发与营销专业求职信范文
2014/05/01 职场文书
中学生2014国庆节演讲稿:不屈的民族
2014/09/21 职场文书
玄武湖导游词
2015/02/05 职场文书
JS中如何优雅的使用async await详解
2021/10/05 Javascript