JS--我发现,原来你是这样的JS:面向对象编程OOP[3]--(JS继承)
一、面向对象编程(继承)
这篇博客是面向对象编程的第三篇,JS继承。继承顾名思义,就是获取父辈的各种"财产"(属性和方法)。
怎么实现继承?
我们的JavaScript比较特别了,主要通过原型链实现继承的。
下面介绍各种实现继承的方式:原型链继承,借用构造函数,组合继承,原型式继承,寄生式继承,寄生组合式继承。
二、实现继承方式
1.原型链方式
原型我们都知道,每个构造函数都有一个原型对象(prototype),用于存放共享的属性方法。
原型链继承原理:我们要继承一个父类,那就把这个子类的原型对象指向父类的实例就行了。
a.代码:
//创建父类构造函数
function Father(){
this.fristName = "Father";
}
//给父类的原型对象添加方法
Father.prototype.getFatherName = function(){
return this.fristName ;
}
//创建子类构造函数
function Son(){
this.name = 'jack';
}
//重点这行:把子类的原型对象指向父类实例实现继承
Son.prototype = new Father();
//我们还可以为子类添加原型方法
Son.prototype.getName = function(){
return this.name;
}
//测试一下,创建一个子类的实例
var s1 = Son();
s1.getFaterName(); //Father 继承了爸爸的名字了!
b.一些特别注意的地方:
1.上面我们看到为子类原型添加方法getName,它在子类原型对象指向父类实例后,也一定要在这句话后面,如果在前面的话,这方法会被覆盖的。因为子类原型对象指向父类实例时相当于重写子类原型对象。
2.为子类原型添加方法式不能用字面量的方式,这样会重写已经继承类父类的原型对象。
c.原型链继承的问题:
很明显的,继承的属性方法都在子类的原型对象里面,那么里面的属性方法都是共享的,这明显不是符合我们平常要求。
2.借用构造函数
借?是的,借一下父类的构造函数,怎么借?那就使用call或者apply方法吧。
借用构造函数原理: 就在子类创建构造函数时内部调用父类的构造函数。
代码演示:
//父类(父类是相对来说的,大家注意一下噢)
function Father(){
this.colors = ['red','green'];
}
//子类
function Son(){
//重点这行:内部调用父类构造函数,this是指向未来创建Son的实例
Father.call(this);
}
//测试一下,继承了父类的属性了,并且属性不会共享噢
var s1 = new Son();
s1.colors.push('blue');
console.log(s1.colors); ['red','green','blue']
//s2实例的colors不会被影响
var s2 = new Son();
console.log(s2.colors); ['red','green']
这里的借用构造函数可以实现属性和方法不会被共享,因为属性在构造函数中,而不是在原型对象中。但是这也说明这种方式没办法共享方法的。
3.组合继承(最常用)
组合继承=原型链继承+借用构造函数继承,没错就结合两种方法就好了。
组合继承原理:实例共享的属性方法:我就原型链继承;实例私用的属性方法:我就借用构造函数(结合两者的优点)
代码演示:
//父类
function Father(name){
this.name = name;
this.colors = ['red','green'];
}
//父类原型对象
Father.pototype.sayName = function(){
console.log(this.name);
}
//子类
function Son(name,age){
//借用父类构造函数
Father.call(this,name);
this.age = age;
}
//子类原型指向父类实例,原型链继承
Son.prototype = new Father();
Son.prototype.constructor = Son; //让子类的原型只向子类构造函数
Son.prototype.sayAge = function(){
console.log(this.age);
}
//创建实例对象
var s1 = new Son('Mc ry',22);
var s2 = new Son('yy',20);
//继承了父类方法,sayName是共享的
s1.sayName(); //mc ry
s2.sayName(); //yy
//继承colors,每个实例私有
s1.colors.push('blue');
console.log(s1.colors); ['red','green','blue']
console.log(s2.colors); ['red','green']
可能有的疑惑:在原型链继承那里,子类原型对象指向类父类的实例,应该继承了所有的属性和方法啊,那应该都是共享的,但为什么colors,name属性不会共享呢?
原因:在调用借用构造函数时,属性在子类新实例中创建了,也就是在子类实例中已经有的父类属性就不用继续到原型对象中查找了,也就是屏蔽了,所以不会共享了。
4.原型式继承
这是道格拉斯·克罗克福提出的一个方式,他提出一个函数object() 来简单实现不用构造函数的继承
代码演示:
//object函数,传入一个对象
function object(o){
//创建一个空的构造函数
function F(){};
//这个空构造函数的原型对象指向o这个对象
F.prototype = o ;
//返回F的实例对象
return new F();
}
认真读这段代码可以知道,这个函数最终返回一个对象,这个对象拥有传入对象o的全部属性和方法。从而实现了继承。
//一个对象
var person = {
name : 'ry',
sayName : function(){
console.log(this.name);
}
}
//children对象继承了person对象所有的属性方法
var children = object(person);
原型式继承方式的出现,可以不用创建构造函数都能继承另一个对象的属性方法。但是很明显,所用属性都是共享的。
ES5中有个object.create()方法的作用和上面的object()一样的。
5.寄生式的继承
寄生式其实和利用了原型式,都要使用到object函数,但不同的是寄生式中新的对象能够添加自己的方法。
function creatAnother(o){
//调用object继承o
var obj = object(o);
//还可以添加自己想要的方法
obj.sayHi = function (){
console.log('hi');
}
//返回这个对象
retrun obj;
}
//新的对象继承了person
var anotherPerson = creatAnother(person);
//继承后能使用person的方法
anotherPerson.sayHi(); //"hi"
6.寄生组合式继承(最理想)
上面组合式方式中虽然是最常用的,但有追求的还是会继续优化它。因为组合方式中也有不够好的地方:
一方面:我们可以看到调用了两次父类的构造函数。(一次是原型链继承中子类原型对象指向父类实例时,一次是借用构造函数中)
另一方面:就是上面疑惑,子类的原型中拥有父类已经有全部属性,但我们又要在调用子类构造函数时重写部分属性。
所以寄生组合方式就解决了上面,通过一个函数来代替组合方式中的原型链继承。
代码演示:
//主要是这个函数,实现子类原型对象只继承父类原型对象中的属性
function inheritPrototype(subType,superType){
//利用上面的Object函数,将父类的原型赋予prototype变量
var prototype = object(superType.prototype);
//将prototype的构造函数重新指向子类
prototype.constructor = subType;
//将prototype给sub的原型对象
subType.prototype = prototype;
}
//将前面的组合继承改写
//父类
function Father(name){
this.name = name;
this.colors = ['red','green'];
}
//父类原型对象
Father.pototype.sayName = function(){
console.log(this.name);
}
//子类
function Son(name,age){
//借用父类构造函数
Father.call(this,name);
this.age = age;
}
//(这行用inheritPrototype函数替换)子类原型指向父类实例,原型链继承
inheritPrototype(Son, Father);
Son.prototype.sayAge = function(){
console.log(this.age);
}
寄生组合式的继承方式是最理想的方式,它使得子类构造函数继承父类构造函数的属性,子类的原型对象继承父类原型对象的属性和方法。
三、小结
1.这次博客讲述了在js中是如何实现继承的,有很多中方式,但主要的是组合方式和寄生组合方式。继承后我们能够使用父类的属性和方法,增加了代码的重用性。
2.了解js继承作用:有助于我们去阅读一些框架的源码,可能本次代码有点难以理解,我打上了大量的注释,供大家一起阅读,还是那句话,多看几遍,其义自见。如果觉得有收获,就点个赞吧,关注我吧。
- 细心的朋友可能发现,我把文章的样式修改了,类似Markdown风格咯,看起来比较清爽,舒服。
同系列几篇:
第一篇:JavaScript--我发现,原来你是这样的JS(一)(初识)
JS--我发现,原来你是这样的JS:面向对象编程OOP[1]--(理解对象和对象属性类型)
JS--我发现,原来你是这样的JS:面向对象编程OOP[2]--(创建你的那个对象吧)
我发现,原来你是这样的JS全部文章汇总(点击此处)
本文出自博客园:http://www.cnblogs.com/Ry-yuan/
作者:Ry(渊源远愿)
欢迎转载,转载请标明出处,保留该字段。
JS--我发现,原来你是这样的JS:面向对象编程OOP[3]--(JS继承)的更多相关文章
- JavaScript--我发现,原来你是这样的JS:面向对象编程OOP[2]--(创建你的那个对象吧)
一.介绍 我们继续面向对象吧,这次是面向对象编程的第二篇,主要是讲创建对象的模式,希望大家能从博客中学到东西. 时间过得很快,还是不断的学习吧,为了自己的目标. 二.创建对象 1.前面的创建对象方式 ...
- JS--我发现,原来你是这样的JS:面向对象编程OOP[2]--(创建你的那个对象吧)
一.介绍 我们继续面向对象吧,这次是面向对象编程的第二篇,主要是讲创建对象的模式,希望大家能从博客中学到东西. 时间过得很快,还是不断的学习吧,为了自己的目标. 二.创建对象 1.前面的创建对象方式 ...
- 如何把js的代码写的更加容易维护(一)--面向对象编程
总是头疼javascript的代码写起来不可维护,那么看看下面的代码: (function (w, $) { var app = { init: function () { var me = this ...
- JavaScript--我发现,原来你是这样的JS:面向对象编程OOP[1]--(理解对象和对象属性类型)
一.介绍 老铁们,这次是JS的面向对象的编程OOP(虽然我没有对象,心累啊,但是可以自己创建啊,哈哈). JS高程里第六章的内容,这章内容在我看来是JS中很难理解的一部分.所以分成三篇博客来逐个理清. ...
- JS--我发现,原来你是这样的JS:面向对象编程OOP[1]--(理解对象和对象属性类型)
一.介绍 老铁们,这次是JS的面向对象的编程OOP(虽然我没有对象,心累啊,但是可以自己创建啊,哈哈). JS高程里第六章的内容,这章内容在我看来是JS中很难理解的一部分.所以分成三篇博客来逐个理清. ...
- JS 学习笔记 (七) 面向对象编程OOP
1.前言 创建对象有很多种方法,最常见的是字面量创建和new Object()创建.但是在需要创建多个相同结构的对象时,这两种方法就不太方便了. 如:创建多个学生信息的对象 let tom = { n ...
- JS面向对象编程(进阶理解)
JS 面向对象编程 如何创建JS对象 JSON语法声明对象(直接量声明对象) var obj = {}; 使用 Object 创建对象 var obj = new Object(); JS对象可以后期 ...
- JS - ES5与ES6面向对象编程
1.面向对象 1.1 两大编程思想 1.2 面向过程编程 POP(Process-oriented programming) 1.3 面向对象编程 OOP (Object Oriented Progr ...
- js面向对象编程:if中可以使用那些作为判断条件呢?
作者来源http://www.2cto.com/kf/201407/314978.html搬运 在所有编程语言中if是最长用的判断之一,但在js中到底哪些东西可以在if中式作为判断表达式呢? 例如如何 ...
随机推荐
- Spring读书笔记——bean加载
我们的日常开发几乎离不开Spring,他为我们的开发带来了很大的便捷,那么Spring框架是如何做到方便他人的呢.今天就来说说bean如何被加载加载. 我们在xml文件中写过太多类似这样的bean声明 ...
- 简单Elixir游戏服设计- 游戏玩法介绍
抄以前的,做了点修改. 到目前为止,我们完成了玩家的数据和进程建模,现在介绍游戏玩法. 为什么我们还不做客户端接入.协议指定呢?为什么还没有网关和数据存储呢.在我接手的游戏, 这些通常已经定下来了,我 ...
- ubuntu 11.04侧边栏怎么添加图标
打开想添加的软件,图标会出现在侧边栏,右击之,点Keep In Launcher即可
- Python日期时间Date/Time
Python程序可以处理多种方式的日期和时间.日期格式之间的转换是一种常见计算机的杂活. Python的时间和日历模块,能帮助处理日期和时间. Tick是什么? 为时间间隔,以秒为单位的浮点数.从“新 ...
- KM算法新识
看了很多写的好的文章,但是针对代码注释来讲,这篇文章最合适. 如果人生会有很长,愿你的荣耀永不散场--wenr大牛. #include ...
- 用C#实现字符串相似度算法(编辑距离算法 Levenshtein Distance)
在搞验证码识别的时候需要比较字符代码的相似度用到"编辑距离算法",关于原理和C#实现做个记录. 据百度百科介绍: 编辑距离,又称Levenshtein距离(也叫做Edit Dist ...
- 虚拟机+桥接模式+Host-only模式 搭建完美的Windows下Linux开发环境
相信有很多鞋童和我一个样是一枚Linux码农(我现在还是嵌入式方向). 做Linux开发势必需要一个Linux环境, 大多数开发者会首先选择一个Linux发行版, 其中Ubuntu LTS版本应该是选 ...
- EditPlus行首行尾批量添加字符 以及其它常用正则
打开EditPlus,输入多行数据,快捷键ctrl+h 打开替换窗口,选择"正则表达式"替换 行首批量添加 查找"^" 替换为"我是行首aaa&q ...
- 通信技术:SSE设计方案(一)--- 前端Server-Sent Events概念讲解和基础类库完善发布
好了,开篇还是要扯扯的,否则感觉这个技术讲的么有那么冻人,嗯,这个晚上是有点冷了,秋衣秋裤大家都该加起来了,反正我不帮你买,妹子除外,嘻嘻. 之前几篇博客,研究前端通信技术的第一层ajax技术,从最基 ...
- [ZJOI2005]九数码游戏
[ZJOI2005]九数码游戏 题目描述 输入输出格式 输入格式: 输入文件中包含三行三列九个数,同行的相邻两数用空格隔开,表示初始状态每个方格上的数字.初始状态不会是目标状态. 输出格式: 如果目标 ...