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 相关文章推荐
node.js中的events.emitter.listeners方法使用说明
Dec 10 Javascript
针对JavaScript中this指向的简单理解
Aug 26 Javascript
JavaScript中常用的验证reg
Oct 13 Javascript
完美解决jQuery的hover事件在IE中不停闪动的问题
Feb 10 Javascript
EasyUI为Numberbox添加blur事件的方法
Mar 05 Javascript
javascript中this用法实例详解
Apr 06 Javascript
Vuex 入门教程
Jan 10 Javascript
详解如何在vue项目中引入elementUI组件
Feb 11 Javascript
vue-cli3 配置开发与测试环境详解
May 17 Javascript
详解vuex数据传输的两种方式及this.$store undefined的解决办法
Aug 26 Javascript
element的el-table中记录滚动条位置的示例代码
Nov 06 Javascript
vue 限制input只能输入正数的操作
Aug 05 Javascript
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
mongo Table类文件 获取MongoCursor(游标)的实现方法分析
2013/07/01 PHP
php无限遍历目录示例
2014/02/21 PHP
php使用ffmpeg获取视频信息并截图的实现方法
2016/05/03 PHP
基本jquery的控制tabs打开的数量的代码
2010/10/17 Javascript
javascript AOP 实现ajax回调函数使用比较方便
2010/11/20 Javascript
javascript模拟select,jselect的方法实现
2012/11/08 Javascript
jquery 页面滚动到指定DIV实现代码
2013/09/25 Javascript
js中的onchange和onpropertychange (onchange无效的解决方法)
2014/03/08 Javascript
JS实现的另类手风琴效果网页内容切换代码
2015/09/08 Javascript
在ASP.NET MVC项目中使用RequireJS库的用法示例
2016/02/15 Javascript
EasyUi中的Combogrid 实现分页和动态搜索远程数据
2016/04/01 Javascript
JavaScript 冒泡排序和选择排序的实现代码
2016/09/03 Javascript
jquery实现自适应banner焦点图
2017/02/16 Javascript
nodejs入门教程四:URL相关模块用法分析
2017/04/24 NodeJs
jQuery使用ajax_动力节点Java学院整理
2017/07/05 jQuery
微信小程序进入广告实现代码实例
2019/09/19 Javascript
js 计数排序的实现示例(升级版)
2020/01/12 Javascript
vue实现户籍管理系统
2020/05/29 Javascript
Python 条件判断的缩写方法
2008/09/06 Python
python从入门到精通(DAY 3)
2015/12/20 Python
使用Python的urllib2模块处理url和图片的技巧两则
2016/02/18 Python
基于python实现在excel中读取与生成随机数写入excel中
2018/01/04 Python
python opencv实现切变换 不裁减图片
2018/07/26 Python
Python使用py2neo操作图数据库neo4j的方法详解
2020/01/13 Python
将tensorflow模型打包成PB文件及PB文件读取方式
2020/01/23 Python
Python用户自定义异常的实现
2020/12/25 Python
马来西亚领先的在线礼品店:Giftr
2018/08/23 全球购物
Collection和Collections的区别
2016/05/02 面试题
EJB包括(SessionBean,EntityBean)说出他们的生命周期,及如何管理事务的
2015/07/24 面试题
int和Integer有什么区别
2013/05/25 面试题
Ruby中的保护方法和私有方法与一般面向对象程序设计语言的一样吗
2013/05/01 面试题
高二学生评语大全
2014/04/25 职场文书
竞赛口号大全
2014/06/16 职场文书
2014年机关后勤工作总结
2014/12/16 职场文书
慰问信模板
2015/02/14 职场文书
vue中的可拖拽宽度div的实现示例
2022/04/08 Vue.js