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 相关文章推荐
让IE8支持DOM 2(不用框架!)
Dec 31 Javascript
js网页版计算器的简单实现
Jul 02 Javascript
JS通过分析userAgent属性来判断浏览器的类型及版本
Mar 28 Javascript
被遗忘的javascript的slice() 方法
Apr 20 Javascript
jQuery的position()方法详解
Jul 19 Javascript
微信小程序 标签传入数据
May 08 Javascript
javascript函数的节流[throttle]与防抖[debounce]
Nov 15 Javascript
jQuery判断自定义属性data-val用法示例
Jan 07 jQuery
VUE 单页面使用 echart 窗口变化时的用法
Jul 30 Javascript
vue移动端写的拖拽功能示例代码
Sep 09 Javascript
vue+Element-ui实现登录注册表单
Nov 17 Javascript
vue 中this.$set 动态绑定数据的案例讲解
Jan 29 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
ThinkPHP3.1新特性之G方法的使用
2014/06/19 PHP
ThinkPHP模板之变量输出、自定义函数与判断语句用法
2014/11/01 PHP
Laravel6.18.19如何优雅的切换发件账户
2020/06/14 PHP
PHP使用POP3读取邮箱接收邮件的示例代码
2020/07/08 PHP
用正则获取指定路径文件的名称
2007/02/27 Javascript
JavaScript 模仿vbs中的 DateAdd() 函数的代码
2007/08/13 Javascript
基于JQuery实现CheckBox全选全不选
2011/06/27 Javascript
基于jQuery+HttpHandler实现图片裁剪效果代码(适用于论坛, SNS)
2011/09/02 Javascript
文本框中禁止非数字字符输入比如手机号码、邮编
2013/08/19 Javascript
javascript 终止函数执行操作
2014/02/14 Javascript
DOM操作一些常用的属性汇总
2015/03/13 Javascript
js如何判断输入字符串长度
2015/12/16 Javascript
浅谈JS之iframe中的窗口
2016/09/13 Javascript
解决bootstrap中下拉菜单点击后不关闭的问题
2018/08/10 Javascript
解决vue-quill-editor上传内容由于图片是base64的导致字符太长的问题
2018/08/20 Javascript
JavaScript JMap类定义与使用方法示例
2019/01/22 Javascript
在Vue项目中使用snapshot测试的具体使用
2019/04/16 Javascript
jQuery pager.js 插件动态分页功能实例分析
2019/08/02 jQuery
node.js中fs文件系统模块的使用方法实例详解
2020/02/13 Javascript
下载安装setuptool和pip linux安装pip    
2014/01/24 Python
PyCharm下载和安装详细步骤
2019/12/17 Python
Python读取表格类型文件代码实例
2020/02/17 Python
Python计算指定日期是今年的第几天(三种方法)
2020/03/26 Python
python模拟斗地主发牌
2020/04/22 Python
python中time包实例详解
2021/02/02 Python
CSS3实现类似翻书效果的过渡动画的示例代码
2019/09/06 HTML / CSS
航空大学应届生求职信
2013/11/10 职场文书
出纳会计岗位职责
2014/03/12 职场文书
养成教育经验材料
2014/05/26 职场文书
企业领导班子四风对照检查材料
2014/09/27 职场文书
党的群众路线教育实践活动学习计划
2014/11/03 职场文书
教师纪律作风整顿心得体会
2016/01/23 职场文书
课改心得体会范文
2016/01/25 职场文书
中秋节作文(五年级)之关于月亮
2019/09/11 职场文书
mysql left join快速转inner join的过程
2021/06/30 MySQL
css样式important规则的正确使用方式
2022/06/10 HTML / CSS