javascript 面向对象全新理练之原型继承


Posted in Javascript onDecember 03, 2009

首先创建一个父类的实例化对象,然后将该对象赋给子类的 prototype 属性。
这样,父类中的所有公有实例成员都会被子类继承。并且用 instanceof 运算符判断时,子类的实例化对象既属于子类,也属于父类。

然后将子类本身赋值给它的 prototype 的 constructor 属性。(注意:这里赋值的时候是没有 () 的!)
这一步是为了保证在查看子类的实例化对象的 constructor 属性时,看到的是子类的定义,而不是其父类的定义。

接下来,通过对 o.method1() 调用的结果我们会看到,子类继承来的公有实例方法中,如果调用了私有实例字段或者私有实例方法,则所调用的这些私有实例成员是属于父类的。

同样,通过对 o.method2() 调用的结果我们看到,子类中定义的实例方法,如果调用了私有实例字段或者私有实例方法,则所调用的这些私有实例成员是属于子类的。

通过对 o.method() 调用的结果我们看到,定义在父类原型上的方法,会被子类继承。
通过对 o.method3() 调用的结果我们看到,子类中定义的实例方法是不能访问父类中定义的私有实例成员的。
最后,通过对 subClass.staticMethod() 调用的结果我们看到,静态成员是不会被继承的。

2.4 调用继承法
调用继承的本质是,在子类的构造器中,让父类的构造器方法在子类的执行上下文上执行,父类构造器方法上所有通过 this 方式操作的内容实际上都都是操作的子类的实例化对象上的内容。因此,这种做法仅仅为了减少重复代码的编写。

function parentClass() { 
// private field 
var x = "I'm a parentClass field!"; 
// private method 
function method1() { 
alert(x); 
alert("I'm a parentClass method!"); 
} 
// public field 
this.x = "I'm a parentClass object field!"; 
// public method 
this.method1 = function() { 
alert(x); 
alert(this.x); 
method1(); 
} 
} 
parentClass.prototype.method = function () { 
alert("I'm a parentClass prototype method!"); 
} parentClass.staticMethod = function () { 
alert("I'm a parentClass static method!"); 
} 
function subClass() { 
// inherit 
parentClass.call(this); 
// private field 
var x = "I'm a subClass field!"; 
// private method 
function method2() { 
alert(x); 
alert("I'm a subClass method!"); 
} 
// public field 
this.x = "I'm a subClass object field!"; 
// public method 
this.method2 = function() { 
alert(x); 
alert(this.x); 
method2(); 
} 
this.method3 = function() { 
method1(); 
} 
} 
// test 
var o = new subClass(); 
alert(o instanceof parentClass); // false 
alert(o instanceof subClass); // true 
alert(o.constructor); // function subClass() {...} 
o.method1(); // I'm a parentClass field! 
// I'm a subClass object field! 
// I'm a parentClass field! 
// I'm a parentClass method! 
o.method2(); // I'm a subClass field! 
// I'm a subClass object field! 
// I'm a subClass field! 
// I'm a subClass method! 
o.method(); // Error!!! 
o.method3(); // Error!!! 
subClass.staticMethod(); // Error!!!

上面这个例子很好的反映出了如何利用调用继承法来实现继承。
利用调用继承的关键只有一步操作:
就是在子类定义时,通过父类的 call 方法,将子类的 this 指针传入。使父类方法在子类上下文中执行。
这样,父类中的所有在父类内部通过 this 方式定义的公有实例成员都会被子类继承。
用 instanceof 运算符判断时,子类的实例化对象只属于子类,不属于父类。
查看子类的实例化对象的 constructor 属性时,看到的是子类的定义,不是其父类的定义。
接下来,通过对 o.method1() 和 o.method2() 调用的结果跟原型继承法的调用结果是相同的,所说明的问题也是一样的,这里不再重复。
通过对 o.method() 调用的结果我们看到,定义在父类原型上的方法,不会被子类继承。
通过对 o.method3() 调用的结果我们看到,子类中定义的实例方法同样不能访问父类中定义的私有实例成员的。
最后,通过对 subClass.staticMethod() 调用的结果我们看到,静态成员同样不会被继承的。
最后,还有一点,在这个例子中没有体现出来,就是通过调用继承法,可以实现多继承。也就是说,一个子类可以从多个父类中继承通过 this 方式定义在父类内部的所有公有实例成员。
作为一种弱类型语言,javascript 提供了丰富的多态性,javascript 的多态性是其它强类型面向对象语言所不能比的。
多态
重载和覆盖
先来说明一下重载和覆盖的区别。重载的英文是 overload,覆盖的英文是 override。发现网上大多数人把 override 当成了重载,这个是不对的。重载和覆盖是有区别的。
重载的意思是,同一个名字的函数(注意这里包括函数)或方法可以有多个实现,他们依靠参数的类型和(或)参数的个数来区分识别。
而覆盖的意思是,子类中可以定义与父类中同名,并且参数类型和个数也相同的方法,这些方法的定义后,在子类的实例化对象中,父类中继承的这些同名方法将被隐藏。
重载
javascript 中函数的参数是没有类型的,并且参数个数也是任意的,例如,尽管你可以定义一个:
function add(a, b) { 
return a + b; 
}

这样的函数,但是你仍然可以再调用它是带入任意多个参数,当然,参数类型也是任意的。至于是否出错,那是这个函数中所执行的内容来决定的,javascript 并不根据你指定的参数个数和参数类型来判断你调用的是哪个函数。
因此,要定义重载方法,就不能像强类型语言中那样做了。但是你仍然可以实现重载。就是通过函数的 arguments 属性。例如:
function add() { 
var sum = 0; 
for (var i = 0; i < arguments.length; i++) { 
sum += arguments[i]; 
} 
return sum; 
}

这样你就实现了任意多个参数加法函数的重载了。
当然,你还可以在函数中通过 instanceof 或者 constructor 来判断每个参数的类型,来决定后面执行什么操作,实现更为复杂的函数或方法重载。总之,javascript 的重载,是在函数中由用户自己通过操作 arguments 这个属性来实现的。
覆盖
实现覆盖也很容易,例如:
function parentClass() { 
this.method = function() { 
alert("parentClass method"); 
} 
} 
function subClass() { 
this.method = function() { 
alert("subClass method"); 
} 
} 
subClass.prototype = new parentClass(); 
subClass.prototype.constructor = subClass; var o = new subClass(); 
o.method();

这样,子类中定义的 method 就覆盖了从父类中继承来的 method 方法了。
你可能会说,这样子覆盖是不错,但 java 中,覆盖的方法里面可以调用被覆盖的方法(父类的方法),在这里怎么实现呢?也很容易,而且比 java 中还要灵活,java 中限制,你只能在覆盖被覆盖方法的方法中才能使用 super 来调用次被覆盖的方法。我们不但可以实现这点,而且还可以让子类中所有的方法中都可以调用父类中被覆盖的方法。看下面的例子:
function parentClass() { 
this.method = function() { 
alert("parentClass method"); 
} 
} 
function subClass() { 
var method = this.method; 
this.method = function() { 
method.call(this); 
alert("subClass method"); 
} 
} 
subClass.prototype = new parentClass(); 
subClass.prototype.constructor = subClass; var o = new subClass(); 
o.method();

你会发现,原来这么简单,只要在定义覆盖方法前,定义一个私有变量,然后把父类中定义的将要被覆盖的方法赋给它,然后我们就可以在后面继续调用它了,而且这个是这个方法是私有的,对于子类的对象是不可见的。这样跟其它高级语言实现的覆盖就一致了。

最后需要注意,我们在覆盖方法中调用这个方法时,需要用 call 方法来改变执行上下文为 this(虽然在这个例子中没有必要),如果直接调用这个方法,执行上下文就会变成全局对象了。

Javascript 相关文章推荐
尽可能写&quot;友好&quot;的&quot;Javascript&quot;代码
Jan 09 Javascript
js过滤数组重复元素的方法
Sep 05 Javascript
简体中文转换繁体中文(实现代码)
Dec 25 Javascript
kindeditor编辑器点中图片滚动条往上顶的bug
Jul 05 Javascript
在Docker快速部署Node.js应用的详细步骤
Sep 02 Javascript
JS实现简单短信验证码界面
Aug 07 Javascript
Angular4实现鼠标悬停3d倾斜效果
Oct 25 Javascript
vue2.0 elementUI制作面包屑导航栏
Feb 22 Javascript
Postman模拟发送带token的请求方法
Mar 31 Javascript
Vue页面切换和a链接的本质区别详解
Nov 12 Javascript
javascript设计模式 ? 备忘录模式原理与用法实例分析
Apr 21 Javascript
vue 实现基础组件的自动化全局注册
Dec 25 Vue.js
javascript 面向对象全新理练之继承与多态
Dec 03 #Javascript
javascript 面向对象全新理练之数据的封装
Dec 03 #Javascript
jquery的ajax从纯真网(cz88.net)获取IP地址对应地区名
Dec 02 #Javascript
jQuery 跨域访问问题解决方法
Dec 02 #Javascript
IE与firefox下Dhtml的一些区别小结
Dec 02 #Javascript
checkbox全选/取消全选以及checkbox遍历jQuery实现代码
Dec 02 #Javascript
两种WEB下的模态对话框 (asp.net或js的分别实现)
Dec 02 #Javascript
You might like
定制404错误页面,并发信给管理员的程序
2006/10/09 PHP
php is_file 判断给定文件名是否为一个正常的文件
2010/05/10 PHP
php 生成签名及验证签名详解
2016/10/26 PHP
PHP数组内存利用率低和弱类型详细解读
2017/08/10 PHP
基于php流程控制语句和循环控制语句(讲解)
2017/10/23 PHP
PHP实现通过二维数组键值获取一维键名操作示例
2019/10/11 PHP
DEFER怎么用?
2006/07/01 Javascript
国外Lightbox v2.03.3 最新版 下载
2007/10/17 Javascript
JavaScript监测ActiveX控件是否已经安装过的代码
2008/09/02 Javascript
利用jquery操作select下拉列表框的代码
2010/06/04 Javascript
一个报数游戏js版(约瑟夫环问题)
2010/08/05 Javascript
javascrip客户端验证文件大小及文件类型并重置上传
2011/01/12 Javascript
JS保留两位小数 四舍五入函数的小例子
2013/11/20 Javascript
给事件响应函数传参数的四种方式小结
2013/12/05 Javascript
深入理解Javascript中this的作用域
2014/08/12 Javascript
jquery mobile页面跳转后样式丢失js失效的解决方法
2014/09/06 Javascript
JavaScript中Boolean对象的属性解析
2015/10/21 Javascript
vue-router两种模式区别及使用注意事项详解
2019/08/01 Javascript
推荐几个不错的console调试技巧实现
2019/12/20 Javascript
Django框架中方法的访问和查找
2015/07/15 Python
Python 读写文件和file对象的方法(推荐)
2016/09/12 Python
详解django中自定义标签和过滤器
2017/07/03 Python
Python制作豆瓣图片的爬虫
2017/12/28 Python
python按综合、销量排序抓取100页的淘宝商品列表信息
2018/02/24 Python
Numpy中转置transpose、T和swapaxes的实例讲解
2018/04/17 Python
Python访问MongoDB,并且转换成Dataframe的方法
2018/10/15 Python
python2.7实现邮件发送功能
2018/12/12 Python
详解torch.Tensor的4种乘法
2020/09/03 Python
Ellos丹麦:时尚和服装在线
2016/09/19 全球购物
金智子午JAVA面试题
2015/09/04 面试题
求职简历的自我评价
2014/01/31 职场文书
2015年新教师工作总结
2015/04/28 职场文书
晚会开场白和结束语
2015/05/29 职场文书
党支部评议意见
2015/06/02 职场文书
MySQL中你可能忽略的COLLATION实例详解
2021/05/12 MySQL
Java如何实现树的同构?
2021/06/22 Java/Android