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 相关文章推荐
Code:loadScript( )加载js的功能函数
Feb 02 Javascript
jQuery使用手册之 事件处理
Mar 24 Javascript
新浪中用来显示flash的函数
Apr 02 Javascript
JavaScript Array Flatten 与递归使用介绍
Oct 30 Javascript
关于jquery的多个选择器的使用示例
Oct 18 Javascript
jQuery基础知识小结
Dec 22 Javascript
wangEditor编辑器失去焦点后仍然可以在原位置插入图片分析
May 06 Javascript
Javascript仿新浪游戏频道鼠标悬停显示子菜单效果
Aug 21 Javascript
Js调用Java方法并互相传参的简单实例
Aug 11 Javascript
JS命令模式例子之菜单程序
Oct 10 Javascript
Auto.JS实现抖音刷宝等刷视频app,自动点赞,自动滑屏,自动切换视频功能
May 08 Javascript
在vue中封装的弹窗组件使用队列模式实现方法
Jul 23 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
php字符串截取问题
2006/11/28 PHP
CodeIgniter实现更改view文件夹路径的方法
2014/07/04 PHP
PHP设计模式之观察者模式实例
2016/02/22 PHP
Linux基于php-fpm模式的lamp搭建phpmyadmin的方法
2018/10/25 PHP
PHP文件操作实例总结【文件上传、下载、分页】
2018/12/08 PHP
PHP内部实现打乱字符串顺序函数str_shuffle的方法
2019/02/14 PHP
php xhprof使用实例详解
2019/04/15 PHP
Yii框架中使用PHPExcel的方法分析
2019/07/25 PHP
JavaScript 三种创建对象的方法
2009/10/16 Javascript
jQuery 动态酷效果实现总结
2009/12/27 Javascript
Jquery ajaxStart()与ajaxStop()方法(实例讲解)
2013/12/18 Javascript
鼠标移到图片上变大显示而不是放大镜效果
2014/06/15 Javascript
javascript每日必学之基础入门
2016/02/16 Javascript
JS读取XML文件数据并以table形式显示数据的方法(兼容IE与火狐)
2016/06/02 Javascript
JavaScript ES6中export、import与export default的用法和区别
2017/03/14 Javascript
jQuery插件FusionCharts绘制的2D帕累托图效果示例【附demo源码】
2017/03/28 jQuery
JavaScript+Html5实现按钮复制文字到剪切板功能(手机网页兼容)
2017/03/30 Javascript
vue.js 初体验之Chrome 插件开发实录
2017/05/13 Javascript
使用jQuery实现页面定时弹出广告效果
2017/08/24 jQuery
Vue使用NPM方式搭建项目
2018/10/25 Javascript
python基础教程之面向对象的一些概念
2014/08/29 Python
Python和JavaScript间代码转换的4个工具
2016/02/22 Python
python实现二维码扫码自动登录淘宝
2016/12/27 Python
python中join()方法介绍
2018/10/11 Python
Python数据可视化:幂律分布实例详解
2019/12/07 Python
如何使用python代码操作git代码
2020/02/29 Python
IWOOT美国:新奇的小玩意
2018/04/27 全球购物
日常奢侈品,轻松购物:Verishop
2019/08/20 全球购物
婚庆公司的创业计划书
2014/01/22 职场文书
生物学专业求职信
2014/07/23 职场文书
关于美容院的活动方案
2014/08/14 职场文书
《中国梦我的梦》大学生演讲稿
2014/08/20 职场文书
2014年大班保育员工作总结
2014/12/02 职场文书
红领巾广播站广播稿
2015/08/19 职场文书
祝福语集锦:送给毕业同学祝福语
2019/11/21 职场文书
python和anaconda的区别
2022/05/06 Python