在所有面向对象的编程中,继承是一个重要的话题。一般说来,在设计类的时候,我们希望能减少重复性的代码,并且尽量弱化对象间的耦合(让一个类继承另一个类可能会导致二者产生强耦合)。关于“解耦”是程序设计中另一个重要的话题,本篇重点来看看在javascript如何实现继承。

其它的面向对象程序设计语言都是通过关键字来解决继承的问题(比如extend或inherit等方式)。但是javascript中并没有定义这种实现的机制,如果一个类需要继承另一个类,这个继承过程需要程序员自己通过编码来实现。
一、类式继承的实现
1、创建一个类的方式:
//定义类的构造函数
function Person(name) {
this.name = name || '默认姓名';
}
//定义该类所有实例的公共方法
Person.prototype.getName = function() {
return this.name;
}
var smith = new Person('Smith');
var jacky = new Person('Jacky');
console.log( smith.getName(), jacky.getName() ); //Smith Jacky
2、继承这个类:
这需要分两个步骤来实现,第1步是继承父类构造函数中定义的属性,第2步是继承父类的prototype属性
//定义类的构造函数
function Person(name) {
this.name = name || '默认姓名';
}
//定义该类所有实例的公共方法
Person.prototype.getName = function() {
return this.name;
}
function Author(name, books) {
//继承父类构造函数中定义的属性
//通过改变父类构造函数的执行上下文来继承
Person.call(this, name);
this.books = books;
}
//继承父类对应的方法
Author.prototype = new Person(); //Author.prototype.constructor === Person
Author.prototype.constructor = Author; //修正修改原型链时造成的constructor丢失
Author.prototype.getBooks = function() {
return this.books;
};
//测试
var smith = new Person('Smith');
var jacky = new Author('Jacky', ['BookA', 'BookB']);
console.log(smith.getName()); //Smith
console.log(jacky.getName()); //Jacky
console.log(jacky.getBooks().join(', ')); //BookA, BookB
console.log(smith.getBooks().join(', ')); //Uncaught TypeError: smith.getBooks is not a function
从测试的结果中可以看出,Author正确继承了Person,而且修改Author的原型时,并不会对Person产生影响。这其中的关键一句就是 Author.prototype = new Person(),要与Author.prototype = Person.prototype区分开来。前者产生了一个实例,这个实例有Person.prototype的副本(这里先这么理解,后面有更详细的解析)。后者是指将两者的prototype指向同一个原型对象。
那么,这也意味着每次继承都将产生一个父类的副本,肯定对内存产生消耗,但为了类式继承这个内存开销必须得支付,但还可以做得更节省一点:Author.prototype = new Person()这一句其实多执行了构造函数一次(而这一次其实只需在子类构造函数中执行即可),尤其是在父类的构造函数很庞大时很耗时和内存。修改一下继承的方式,如下:
Author.prototype = (function() {
function F() {}
F.prototype = Person.prototype;
return new F();
})();
如上所示的代码,new时,去掉了对父类的构造函数的调用,节省了一次调用的开销。
3、类式继承显著的特点是每一次实例化对象时,子类都将执行一次父类的构造函数。如果E继承了D,D继承了C,C继承了B,B继承了A,在实例化一个E时,一共要经过几次构造函数的调用呢?
/*继承方法的函数*/
function extend(son, father) {
function F() {}
F.prototype = father.prototype;
son.prototype = new F();
son.prototype.constructor = son;
}
//A类
function A() {
console.log('A()');
}
A.prototype.hello = function() {
console.log('Hello, world.');
}
//B类
function B() {
A.call(this);
console.log('B()');
}
extend(B, A);
//C类
function C() {
B.call(this);
console.log('C()');
}
extend(C, B);
//D类
function D() {
C.call(this);
console.log('D()');
}
extend(D, C);
//E类
function E() {
D.call(this);
console.log('E()');
}
extend(E, D);
//创建一个E的实例
var e = new E(); //A() B() C() D() E()
e.hello(); //hello, world.
5次,这还只是实例化一个E时调用的次数。所以,我们应该尽可能的减少继承的级别。但这并不是说不要使用这种类式继承,而是应该根据自己的应用场合决定采用什么方法。
二、原型式继承
1、先来看一段代码:我们先将之前类式继承中的继承prototype那一段改成另一个函数clone,然后通过字面量创建一个Person,最后让Author变成Person的克隆体。
//这个函数可以理解为克隆一个对象
function clone(object) {
function F() {}
F.prototype = object;
return new F();
}
var Person = {
name: 'Default Name';
getName: function() {
return this.name;
}
}
//接下来让Author变为Person的克隆体
var Author = clone(Person);
问一个问题:clone函数里的new F()为这个实例开辟内存空间来存储object的副本了吗?
按我之前的理解,回答是肯定的。但是,当我继续将代码写下去的时候,奇怪的事情发生了,代码如下:
//接下来让Author变为Person的克隆体
var Author = clone(Person);
Author.books = [];
Author.getBooks = function() {
return this.books.join(', ');
}
//增加一个作者Smith
var Smith = clone(Author);
console.log(Smith.getName(), Smith.getBooks()); //Default Name
Smith.name = 'Smith';
Smith.books.push('<<Book A>>', '<<Book B>>'); //作者写了两本书
console.log(Smith.getName(), Smith.getBooks()); //Smith <<Book A>>, <<Book B>>
//再增加一个作者Jacky
var Jacky = clone(Author);
console.log(Jacky.getName(), Jacky.getBooks()); // Default Name <<Book A>>, <<Book B>>
当我们继续增加作者Jacky时,奇怪的现象发生了!!Jacky的名字依然是Default Name,但是他居然也写两本与Smith一样的书?Jacky的书都还没push呢。到了这里,我想到了引用对象的情况(引用一个对象时,引用的是该对象的内存地址),发生这样的现象,问题肯定出在clone()函数中的new F()这里。
事实上,这个clone中的new F()确实返回了一个新的对象,该对象拥有被克隆对象的所有属性。但这些属性保留的是对被克隆对象中相应属性的引用,而非一个属性副本。换句话说,新对象的属性 与 被克隆的对象的属性指向同一个内存地址(学过C语言的同学应该明白指针类型,这里意义差不多)。
那么为什么上面的代码中,Jacky的书与Smith的书相同了,为什么Jacky的名字却不是Smith而是Default Name呢?这就是Javascript中继承的机制所在,当Smith刚刚继承自Author时,他的属性保留了对Author的属性的引用,一旦我们显示的对Smith的属性重新赋值时,Javascritp引擎就会给Smith的该属性重新划分内存空间来存储相应的值,由于重新划分了内址地址,那么对Smith.name的改写就不会影响到Author.name去了。这就很好的解释了前面的那个问题——为什么Jacky的名字却不是Smith而是Default Name。
2、基于原型继承
通过前面的情况分析,可以看出基于原型继承的方式更能节约内存(只有在需要时候才开辟新的内存空间)。但要注意:基于原型继承时,对象的属性一定要重新赋值后(重新划分内存)再去引用该属性。对于对象的方法,如果有不同的处理方式,我们只需重新定义即可。
下面将前一段代码做一个完整、正确的范例出来,以说明原型继承的特点和使用方式:
//这个函数可以理解为克隆一个对象
function clone(object) {
function F() {}
F.prototype = object;
return new F();
}
var Person = {
name: 'Default Name',
getName: function() {
return this.name;
}
}
//接下来让Author变为Person的克隆体
var Author = clone(Person);
Author.books = [];
Author.getBooks = function() {
return this.books.join(', ');
}
//增加一个作者Smith
var Smith = clone(Author);
Smith.name = 'Smith';
Smith.books = [];
Smith.books.push('<<Book A>>', '<<Book B>>'); //作者写了两本书
console.log(Smith.getName(), Smith.getBooks()); //Smith <<Book A>>, <<Book B>>
//再增加一个作者Jacky
var Jacky = clone(Author);
Jacky.name = 'Jacky';
Jacky.books = [];
Jacky.books.push('<<Book C>>', '<<Book D>>');
console.log(Jacky.getName(), Jacky.getBooks()); // Jacky <<Book C>>, <<Book D>>
三、类式继承与原型式继承的区别与相式之处
1、类式继承中:使用构造函数初始化对象的属性,通过调用父类的构造函数来继承这些属性。通过new 父类的prototype来继承方法。
2、原型式继承中:去掉了构造函数,但需要将对象的属性和方法写一个{}里申明。准确的说,原型式继承就是类式继承中继承父类的prototype方法。
以上所述是小编给大家介绍的Javascript中类式继承和原型式继承的实现方法和区别,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
# javascript 原型继承
# javascript 类式继承
# 的书
# 的是
# 继承了
# 创建一个
# 子类
# 只需
# 掉了
# 写了
# 都将
# 可以看出
# 来实现
# 小编
# 面向对象
# 自己的
# 都是
# 两本书
# 是一个
# 发生了
# 我想
# 这一
相关文章:
网站制作与设计教程,如何制作一个企业网站,建设网站的基本步骤有哪些?
如何选择PHP开源工具快速搭建网站?
电商网站制作多少钱一个,电子商务公司的网站制作费用计入什么科目?
如何用VPS主机快速搭建个人网站?
音乐网站服务器如何优化API响应速度?
历史网站制作软件,华为如何找回被删除的网站?
如何在IIS管理器中快速创建并配置网站?
如何在云指建站中生成FTP站点?
全景视频制作网站有哪些,全景图怎么做成网页?
如何快速搭建高效WAP手机网站?
如何零成本快速生成个人自助网站?
制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?
如何快速生成橙子建站落地页链接?
惠州网站建设制作推广,惠州市华视达文化传媒有限公司怎么样?
正规网站制作公司有哪些,目前国内哪家网页网站制作设计公司比较专业靠谱?口碑好?
外汇网站制作流程,如何在工商银行网站上做外汇买卖?
大连网站制作公司哪家好一点,大连买房网站哪个好?
网站制作的方法有哪些,如何将自己制作的网站发布到网上?
东莞专业网站制作公司有哪些,东莞招聘网站哪个好?
建站DNS解析失败?如何正确配置域名服务器?
家具网站制作软件,家具厂怎么跑业务?
如何在橙子建站中快速调整背景颜色?
专业网站建设制作报价,网页设计制作要考什么证?
北京的网站制作公司有哪些,哪个视频网站最好?
建站主机是什么?如何选择适合的建站主机?
公众号网站制作网页,微信公众号怎么制作?
建站之星如何防范黑客攻击与数据泄露?
品牌网站制作公司有哪些,买正品品牌一般去哪个网站买?
IOS倒计时设置UIButton标题title的抖动问题
如何在服务器上配置二级域名建站?
电商网站制作公司有哪些,1688网是什么意思?
制作网站外包平台,自动化接单网站有哪些?
网站制作话术技巧,网站推广做的好怎么话术?
制作假网页,招聘网的薪资待遇,会有靠谱的吗?一面试又各种折扣?
建站之星后台管理系统如何操作?
如何在Windows服务器上快速搭建网站?
如何获取开源自助建站系统免费下载链接?
TestNG的testng.xml配置文件怎么写
网站插件制作软件免费下载,网页视频怎么下到本地插件?
宝盒自助建站智能生成技巧:SEO优化与关键词设置指南
如何用花生壳三步快速搭建专属网站?
网站制作公司排行榜,抖音怎样做个人官方网站
如何用5美元大硬盘VPS安全高效搭建个人网站?
如何获取免费开源的自助建站系统源码?
电影网站制作价格表,那些提供免费电影的网站,他们是怎么盈利的?
北京网站制作公司哪家好一点,北京租房网站有哪些?
武清网站制作公司,天津武清个人营业执照注销查询系统网站?
高防服务器如何保障网站安全无虞?
网站制作和推广的区别,想自己建立一个网站做推广,有什么快捷方法马上做好一个网站?
建站之星官网登录失败?如何快速解决?
*请认真填写需求信息,我们会在24小时内与您取得联系。