Javascript面向对象编程(二) 构造函数的继承


Posted in Javascript onAugust 28, 2011

今天要介绍的是,如何生成一个"继承"多个对象的实例。
比如,现在有一个"动物"对象的构造函数,

function Animal(){ 


this.species = "动物"; 

}

还有一个"猫"对象的构造函数,
function Cat(name,color){ 


this.name = name; 


this.color = color; 

}

怎样才能使"猫"继承"动物"呢?
1. 构造函数绑定
最简单的方法,大概就是使用call或apply方法,将父对象的构造函数绑定在子对象上,也就是在子对象构造函数中加一行:
function Cat(name,color){ 


Animal.apply(this, arguments); 


this.name = name; 


this.color = color; 

} 

var cat1 = new Cat("大毛","黄色"); 

alert(cat1.species); // 动物

2. prototype模式
更常见的做法,则是使用prototype属性。
如果"猫"的prototype对象,指向一个Animal的实例,那么所有"猫"的实例,就能继承Animal了。
Cat.prototype = new Animal(); 

Cat.prototype.constructor = Cat; 

var cat1 = new Cat("大毛","黄色"); 

alert(cat1.species); // 动物

代码的第一行,我们将Cat的prototype对象指向一个Animal的实例。

Cat.prototype = new Animal();
它相当于完全删除了prototype 对象原先的值,然后赋予一个新值。但是,第二行又是什么意思呢?

Cat.prototype.constructor = Cat;
原来,任何一个prototype对象都有一个constructor属性,指向它的构造函数。也就是说,Cat.prototype 这个对象的constructor属性,是指向Cat的。
我们在前一步已经删除了这个prototype对象原来的值,所以新的prototype对象没有constructor属性,所以我们必须手动加上去,否则后面的"继承链"会出问题。这就是第二行的意思。
总之,这是很重要的一点,编程时务必要遵守。下文都遵循这一点,即如果替换了prototype对象,

o.prototype = {};
那么,下一步必然是为新的prototype对象加上constructor属性,并将这个属性指回原来的构造函数。

o.prototype.constructor = o;
3. 直接继承prototype
由于Animal对象中,不变的属性都可以直接写入Animal.prototype。所以,我们也可以让Cat()跳过 Animal(),直接继承Animal.prototype。
现在,我们先将Animal对象改写:

function Animal(){ } 

Animal.prototype.species = "动物";

然后,将Cat的prototype对象,然后指向Animal的prototype对象,这样就完成了继承。
Cat.prototype = Animal.prototype; 

Cat.prototype.constructor = Cat; 

var cat1 = new Cat("大毛","黄色"); 

alert(cat1.species); // 动物

与前一种方法相比,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。缺点是 Cat.prototype和Animal.prototype现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。
所以,上面这一段代码其实是有问题的。请看第二行

Cat.prototype.constructor = Cat;
这一句实际上把Animal.prototype对象的constructor属性也改掉了!

alert(Animal.prototype.constructor); // Cat
4. 利用空对象作为中介
由于"直接继承prototype"存在上述的缺点,所以可以利用一个空对象作为中介。

var F = function(){}; 

F.prototype = Animal.prototype; 

Cat.prototype = new F(); 

Cat.prototype.constructor = Cat;

F是空对象,所以几乎不占内存。这时,修改Cat的prototype对象,就不会影响到Animal的prototype对象。

alert(Animal.prototype.constructor); // Animal
5. prototype模式的封装函数
我们将上面的方法,封装成一个函数,便于使用。

function extend(Child, Parent) { 


var F = function(){}; 


F.prototype = Parent.prototype; 


Child.prototype = new F(); 


Child.prototype.constructor = Child; 


Child.uber = Parent.prototype; 

}

使用的时候,方法如下
extend(Cat,Animal); 

var cat1 = new Cat("大毛","黄色"); 

alert(cat1.species); // 动物

这个extend函数,就是YUI库如何实现继承的方法。
另外,说明一点。函数体最后一行

Child.uber = Parent.prototype;
意思是为子对象设一个uber属性,这个属性直接指向父对象的prototype属性。这等于是在子对象上打开一条通道,可以直接调用父对象的方法。这一行放在这里,只是为了实现继承的完备性,纯属备用性质。
6. 拷贝继承
上面是采用prototype对象,实现继承。我们也可以换一种思路,纯粹采用"拷贝"方法实现继承。简单说,如果把父对象的所有属性和方法,拷贝进子对象,不也能够实现继承吗?
首先,还是把Animal的所有不变属性,都放到它的prototype对象上。

function Animal(){} 

Animal.prototype.species = "动物";

然后,再写一个函数,实现属性拷贝的目的。
function extend2(Child, Parent) { 


var p = Parent.prototype; 


var c = Child.prototype; 


for (var i in p) { 



c[i] = p[i]; 



} 


c.uber = p; 

}

这个函数的作用,就是将父对象的prototype对象中的属性,一一拷贝给Child对象的prototype对象。
使用的时候,这样写:
extend2(Cat, Animal); 

var cat1 = new Cat("大毛","黄色"); 

alert(cat1.species); // 动物

未完,请继续阅读第三部分《非构造函数的继承》。
(完)
Javascript 相关文章推荐
textarea不能通过maxlength属性来限制字数的解决方法
Sep 01 Javascript
Eclipse配置Javascript开发环境图文教程
Jan 29 Javascript
javascript url几种编码方式详解
Jun 06 Javascript
JS检测是否可以访问公网服务器功能代码
Jun 19 Javascript
Vue.js常用指令的使用小结
Jun 23 Javascript
OkHttp踩坑随笔为何 response.body().string() 只能调用一次
Jan 08 Javascript
详解webpack多页面配置记录
Jan 22 Javascript
jQuery+koa2实现简单的Ajax请求的示例
Mar 06 jQuery
vue-cli3 从搭建到优化的详细步骤
Jan 20 Javascript
Electron 如何调用本地模块的方法
Feb 01 Javascript
easyUI 实现的后台分页与前台显示功能示例
Jun 01 Javascript
vue项目中openlayers绘制行政区划
Dec 24 Vue.js
Javascript 面向对象编程(一) 封装
Aug 28 #Javascript
Javascript继承机制的设计思想分享
Aug 28 #Javascript
有关JavaScript的10个怪癖和秘密分享
Aug 28 #Javascript
JS面向对象编程浅析
Aug 28 #Javascript
用JS实现一个TreeMenu效果分享
Aug 28 #Javascript
JS target与currentTarget区别说明
Aug 28 #Javascript
IE6,IE7,IE8下使用Javascript记录光标选中范围(已补全)
Aug 28 #Javascript
You might like
thinkphp学习笔记之多表查询
2014/07/28 PHP
php简单实现多语言切换的方法
2016/05/09 PHP
音乐播放用的的几个函数
2006/09/07 Javascript
JQuery 图片延迟加载并等比缩放插件
2009/11/09 Javascript
js 刷新页面的代码小结 推荐
2010/04/02 Javascript
js实现兼容IE6与IE7的DIV高度
2010/05/13 Javascript
js获取图片大小的函数代码
2011/09/20 Javascript
jquery实现盒子下拉效果示例代码
2013/09/12 Javascript
jquery select 设置默认选中的示例代码
2014/02/07 Javascript
jQuery中size()方法用法实例
2014/12/27 Javascript
JavaScript实现文字与图片拖拽效果的方法
2015/02/16 Javascript
jquery设置css样式的多种方法(总结)
2017/02/21 Javascript
ES6 javascript中Class类继承用法实例详解
2017/10/30 Javascript
浅谈Node.js 沙箱环境
2018/05/15 Javascript
解决angular2 获取到的数据无法实时更新的问题
2018/08/31 Javascript
解决vue 绑定对象内点击事件失效问题
2018/09/05 Javascript
解决layui 三级联动下拉框更新时回显的问题
2019/09/03 Javascript
JavaScript 链表定义与使用方法示例
2020/04/28 Javascript
js实现搜索提示框效果
2020/09/05 Javascript
ant design vue 表格table 默认勾选几项的操作
2020/10/31 Javascript
[01:32]2014DOTA2西雅图邀请赛 CIS我们有信心进入正赛
2014/07/08 DOTA
python中wx将图标显示在右下角的脚本代码
2013/03/08 Python
Python聊天室实例程序分享
2016/01/05 Python
python requests爬取高德地图数据的实例
2018/11/10 Python
Python3获取拉勾网招聘信息的方法实例
2019/04/03 Python
详解使用python绘制混淆矩阵(confusion_matrix)
2019/07/14 Python
基于python3生成标签云代码解析
2020/02/18 Python
Selenium 滚动页面至元素可见的方法
2020/03/18 Python
Python 实现进度条的六种方式
2021/01/06 Python
AVIS安飞士奥地利租车官网:提供奥地利、欧洲和全世界汽车租赁
2016/11/29 全球购物
法国创作个性化T恤衫和其他定制产品平台:Tostadora
2018/04/08 全球购物
韩国保养品、日本药妆购物网:小三美日
2018/12/30 全球购物
学生会副主席竞聘书
2014/03/31 职场文书
井冈山红色之旅感想
2014/10/07 职场文书
2016年教师学习廉政准则心得体会
2016/01/20 职场文书
使用Golang的channel交叉打印两个数组的操作
2021/04/29 Golang