一、前言

java、C#等正统面向对象语言都会提供类似extend之类的处理类的继承的方法,而javascript并没有提供专门的方法用于继承,在javascript中使用继承需要一点技巧。js中实例的属性和行为是由构造函数和原型两部分组成的,js的继承也分为这两部分。下面给大家分享一下在js中如何实现继承,讲的不对的地方望大家指正!

二、继承构造函数中的属性和行为

我们定义两个类Animal和Bird类,来实现js中类的继承。

//定义Animal类
function Animal(name){
this.name = name;
this.type = 'animal';
}
//Animal原型添加say方法
Animal.prototype.say = function(){
alert("I'm a(an) "+ this.type +", my name is "+ this.name);
};

接下来定义Bird类

//定义Bird类
function Bird(name){
this.name = name;
this.type = 'bird';
} Bird.prototype = { };

我们先让Bird类继承Animal的构造函数中的属性和行为,代码清单如下

//定义Animal类
function Animal(name){
this.name = name;
this.type = 'animal';
}
//Animal原型添加say方法
Animal.prototype = {
say : function(){
alert("I'm a(an) "+ this.type +", my name is "+ this.name);
}
}; //定义Bird类
function Bird(name){
this.name = name;
this.type = 'animal';
} Bird.prototype = { }; //实例化Bird类
var canary = new Bird('yinwu');
alert(canary.type);

程序运行正常,但看不出继承,因为在Bird类的构造函数中是将Animal的属性和行为复制了一份。同时,缺点也很明显,如果Animal类的构造函数有任何改动,Bird类也要手动的同步改动,同样一份代码,复制在程序中不同地方,违反了DRY原则,降低了代码的可维护性。需要改进,改进后的代码清单如下:

//定义Animal类
function Animal(name){
this.name = name;
this.type = 'animal';
}
//Animal原型添加say方法
Animal.prototype = {
say : function(){
alert("I'm a(an) "+ this.type +", my name is "+ this.name);
}
}; //定义Bird类
function Bird(name){
Animal.call(this, name);
} Bird.prototype = { }; //实例化Bird类
var canary = new Bird('yinwu');
alert(canary.type);//animal

我们把Bird类改为了Animal.call(this, name);,也就是通过call()方法实现让Animal内部的this指向Bird类的实例。改进后的代码就实现了构造函数的属性和行为的继承,最后alert(canary.type)弹出的信息为animal。

三、原型的继承

由继承我们知道,继承后子类拥有了父类同样的属性和行为,那么能否将Animal类的原型直接传给Bird类的原型呢?请看如下代码清单:

//定义Animal类
function Animal(name){
this.name = name;
this.type = 'animal';
}
//Animal原型添加say方法
Animal.prototype = {
say : function(){
alert("I'm a(an) "+ this.type +", my name is "+ this.name);
}
}; //定义Bird类
function Bird(name){
Animal.call(this, name);
} Bird.prototype = Animal.prototype; //实例化Bird类
var canary = new Bird('yinwu');
canary.say();//I'm a(an) animal, my name is yinwu

通过把Animal类的原型直接传给Bird类的原型,Bird类拥有了Animal原型中的say行为。如果给Bird类的原型添加fly行为,结果会如何呢?如代码清单:

//定义Animal类
function Animal(name){
this.name = name;
this.type = 'animal';
}
//Animal原型添加say方法
Animal.prototype = {
say : function(){
alert("I'm a(an) "+ this.type +", my name is "+ this.name);
}
}; //定义Bird类
function Bird(name){
Animal.call(this, name);
} Bird.prototype = Animal.prototype;
//Bird类原型添加fly方法
Bird.prototype.fly = function(){
alert("I'm flying more high ");
}
//实例化Bird类
var canary = new Bird('yinwu');
canary.say();//I'm a(an) animal, my name is yinwu
canary.fly();//I'm flying more high
var pig = new Animal('xiaozhu');
pig.fly();//I'm flying more high

程序运行你会发现Animal类也获得了fly行为,pig对象调用fly行为弹出I'm flying more high的提示信息,我们只给Bird类添加fly行为,为什么Animal类也获得了fly行为呢?原因在于”Bird.prototype = Animal.prototype;”这句代码,因为prototype原型本质上是hash对象,赋值时会进行传址,也就是说Bird.prototype原型和Animal.prototype原型指向同一内存地址,对Bird.prototype原型添加fly行为也就相当于为Animal.prototype原型添加了fly行为,这就是pig会fly的原因。如何对代码进行改进呢?请看如下代码清单:

//定义Animal类
function Animal(name){
this.name = name;
this.type = 'animal';
}
//Animal原型添加say方法
Animal.prototype = {
say : function(){
alert("I'm a(an) "+ this.type +", my name is "+ this.name);
}
}; //定义Bird类
function Bird(name){
Animal.call(this, name);
} Bird.prototype = new Animal();
Bird.prototype.constructor = Bird;
//Bird类原型添加fly方法
Bird.prototype.fly = function(){
alert("I'm flying more high ");
}
//实例化Bird类
var canary = new Bird('yinwu');
canary.say();//I'm a(an) animal, my name is yinwu
canary.fly();//I'm flying more high
var pig = new Animal('xiaozhu');
pig.fly();//报错

与前一代码清单相比,把”Bird.prototype = Animal.prototype;”这句代码,改成了”Bird.prototype = new Animal(); Bird.prototype.constructor = Bird;”,这两句什么意思呢? ”Bird.prototype = new Animal();”意思是Bird.prototype原型作为Animal类的实例,那么Bird原型对象中包含了一个指向Animal原型对象的指针;“Bird.prototype.constructor = Bird;”意思是因为“Bird.prototype = new Animal();”时,Bird.prototype.constructor指向了Animal原型对象,将其纠正重新指向Bird。代码运行后你会发现pig.fly()会报错,此fly()行为undefined,那是因为Animal原型中没有fly行为。到此我们就实现了Bird类对Animal类的原型继承。

四、js继承的封装

前面二、三小节中分别实现了构造函数和原型的属性和行为的继承,这节中我们将js中实现继承的过程进行封装,向外提供接口调用。请看下列代码:

//js继承封装成函数并提供接口
function extend(subClass, superClass){
var F = function(){};
F.prototype = superClass.prototype;//F.prototype 原型对象和superClass.prototype原型对象指向同一内存地址
subClass.prototype = new F();//subClass.prototype作为F类的实例
subClass.prototype.constructor = subClass;//将subClass.prototype.constructor重新指向subClass
subClass.superclass = superClass.prototype;//subClass类中定义superclass属性使其指向superClass.prototype
if(superClass.prototype.constructor == Object.prototype.constructor){
superClass.prototype.constructor = superClass;
}
} //定义Animal类
function Animal(name){
this.name = name;
this.type = 'animal';
}
//Animal原型添加say方法
Animal.prototype.say = function(){
alert("I'm a(an) "+ this.type +", my name is "+ this.name);
}; //定义Bird类
function Bird(name){
this.constructor.superclass.constructor.apply(this, arguments);//通过apply方法继承Animal类中的属性和行为
this.type = 'bird';
} //实现对Animal继承
extend(Bird, Animal); Bird.prototype.fly = function(){
alert("I'm flying more high ");
}; //实例化
var canary = new Bird('yinwu');
canary.say();//I'm a(an) animal, my name is yinwu
canary.fly();//I'm flying more high

通过上述方法将js中的继承封装在函数中,要用到继承时直接调用即可。

此文章发布在本人博客园changjianqiu,如需转载本文,请注明来源: http://www.cnblogs.com/changjianqiu/

浅谈js中继承的理解和实现的更多相关文章

  1. 浅谈JS中的闭包

    浅谈JS中的闭包 在介绍闭包之前,我先介绍点JS的基础知识,下面的基础知识会充分的帮助你理解闭包.那么接下来先看下变量的作用域. 变量的作用域 变量共有两种,一种为全局变量,一种为局部变量.那么全局变 ...

  2. 浅谈JS中的!=、== 、!==、===的用法和区别 JS中Null与Undefined的区别 读取XML文件 获取路径的方式 C#中Cookie,Session,Application的用法与区别? c#反射 抽象工厂

    浅谈JS中的!=.== .!==.===的用法和区别   var num = 1;     var str = '1';     var test = 1;     test == num  //tr ...

  3. 浅谈JS中 var let const 变量声明

    浅谈JS中 var let const 变量声明 用var来声明变量会出现的问题: 1. 允许重复的变量声明:导致数据被覆盖 2. 变量提升:怪异的数据访问.闭包问题 3. 全局变量挂载到全局对象:全 ...

  4. 浅谈JS的继承

    JS继承 继承是OO语言中最为人津津乐道的概念,许多OO语言都支持两种方式的继承:接口继承:实现继承. 接口继承:只继承方法签名. 实现继承:继承实际的方法. 由于ES里函数没有签名,所以在ES里面无 ...

  5. 由项目浅谈JS中MVVM模式

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.    背景 最近项目原因使用了durandal.js和knock ...

  6. js架构设计模式——由项目浅谈JS中MVVM模式

    1.    背景 最近项目原因使用了durandal.js和knockout.js,颇有受益.决定写一个比较浅显的总结. 之前一直在用SpringMVC框架写后台,前台是用JSP+JS+标签库,算是很 ...

  7. 浅谈JavaScript中继承的实现

    谈到js中的面向对象编程,都有一个共同点,选择原型属性还是构造函数,两者各有利弊,而就片面的从js的对象创建以及继承的实现两个方面来说,官方所推荐的是两个相结合,各尽其责,各取其长,在前面的例子中,我 ...

  8. 浅谈JS中的高级函数

    在JavaScript中,函数的功能十分强大.它们是第一类对象,也可以作为另一个对象的方法,还可以作为参数传入另一个函数,不仅如此,还能被一个函数返回!可以说,在JS中,函数无处不在,无所不能,堪比孙 ...

  9. 浅谈JS中的typeof和instanceof的区别

    JS中的typeof和instanceof常用来判断一个变量是否为空,或者是什么类型. typeof typeof运算符返回一个用来表示表达式的数据类型的字符串. typeof一般返回以下几个字符串: ...

随机推荐

  1. 树链剖分【p2568】[SDOI2011]染色

    Description 给定一颗有\(n\)个节点的无根树和\(m\)个操作,操作有\(2\)类: 1.将节点\(a\)到节点\(b\)路径上所有点染成颜色\(c\) 2.询问节点\(a\)到节点\( ...

  2. 5、Django实战第5天:首页和登录页面的配置

    从这天开始我们需要用到前端源码,需要的朋友可以进行小额打赏(15元),打赏二维码在博客的右侧,打赏后可以凭截图联系463951510@qq.com,博主收到邮件后会立即回复发送所有源码素材,实战过程中 ...

  3. 【原创】项目管理软件之争,禅道和JIRA大对比

    本文摘要: 一. 产品介绍 二. 界面设计 1. 界面颜色设计 2. 布局结构 三. 功能区别 四. 价格对比 五. 后期服务 六. 优缺点 七. 总结 说到项目管理软件,不得不提的是禅道和JIRA. ...

  4. 【带修莫队】【权值分块】bzoj3196 Tyvj 1730 二逼平衡树

    这题用了三种算法写: 分块+二分:O(n*sqrt(n*log(n)) 函数式权值分块:O(n*sqrt(n)) 带修莫队+权值分块:O(n5/3) 结果……复杂度越高的实际上跑得越快……最后这个竟然 ...

  5. Scala实战高手****第15课:Scala类型参数编程实战及Spark源码鉴赏

    1.Scala的类和方法.函数都可以是泛型 2.上界:表示泛型的类型必须是某种类型或者其类型的子类,语法:<: ,对类型进行限定 3.下界:表示泛型的类型必须是某种类型或者其类型的父类,语法:& ...

  6. webservice_客户端生成工具

    1. axis java -Djava.ext.dirs=lib org.apache.axis.wsdl.WSDL2Java -p com.qunar.flight.flagship.provide ...

  7. Swift,数组

    1.创建(Array)数组(数组内的类型一定要相同,有序的可重复) (1)创建默认值的数组 let array:[Int] array=[Int](repeatElement(3,count:5)) ...

  8. Build a Restful Service

    最近项目中遇到的REST的问题很多,很多情况下受weblogic以及Jdeveloper版本的影响,无法直接从Jdeveloper中生成一个RESTful Service出来,所以基于流行的Sprin ...

  9. gcc static静态编译选项提示错误:/usr/lib/ld:cannot find -lc

    在学习gcc静态库动态库编译的时候选用静态库编译时出错显示:/usr/lib/ld:cannot find -lc 百度:/usr/lib/ld:cannot find -lc多处给的解决方案为: 然 ...

  10. 如何解决nagios安装及运行在的常见错误?(转)

    一.安装nrpe的时候,提示:checking for SSL headers… configure: error: Cannot find ssl headers 解决方法如下: yum -y in ...