[js高手之路]从原型链开始图解继承到组合继承的产生
于javascript原型链的层层递进查找规则,以及原型对象(prototype)的共享特性,实现继承是非常简单的事情
一、把父类的实例对象赋给子类的原型对象(prototype),可以实现继承
function Person(){
this.userName = 'ghostwu';
}
Person.prototype.showUserName = function(){
return this.userName;
}
function Teacher (){}
Teacher.prototype = new Person();
var oT = new Teacher();
console.log( oT.userName ); //ghostwu
console.log( oT.showUserName() ); //ghostwu
通过把父类(Person)的一个实例赋给子类Teacher的原型对象,就可以实现继承,子类的实例就可以访问到父类的属性和方法

如果你不会画这个图,你需要去看下我的这篇文章:[js高手之路]一步步图解javascript的原型(prototype)对象,原型链
第11行,执行oT.userName, 首先去oT对象上查找,很明显oT对象上没有任何属性,所以就顺着oT的隐式原型__proto__的指向查找到Teacher.prototype,
发现还是没有userName这个属性,继续沿着Teacher.prototype.__proto__向上查找,找到了new Person() 这个实例上面有个userName,值为ghostwu
所以停止查找,输出ghostwu.
第12行,执行oT.showUserName前面的过程同上,但是在new Person()这个实例上还是没有查找到showUserName这个方法,继续沿着new Person()的
隐式原型__proto__的指向( Person.prototype )查找,在Person.prototype上找到了showUserName这个方法,停止查找,输出ghostwu.
二、把父类的原型对象(prototype)赋给子类的原型对象(prototype),可以继承到父类的方法,但是继承不到父类的属性
function Person(){
this.userName = 'ghostwu';
}
Person.prototype.showUserName = function(){
return 'Person::showUserName方法';
}
function Teacher (){}
Teacher.prototype = Person.prototype;
var oT = new Teacher();
console.log( oT.showUserName() ); //ghostwu
console.log( oT.userName ); //undefined, 没有继承到父类的userName
因为Teacher.prototype被Person.protoype替换了( 第8行代码 ),所以,Teacher的prototype属性就直接指向了Person.prototype. 所以获取不到Person实例的属性
三、发生继承关系后,实例与构造函数(类)的关系判断
还是通过instanceof和isPrototypeOf判断
function Person(){
this.userName = 'ghostwu';
}
Person.prototype.showUserName = function(){
return this.userName;
}
function Teacher (){}
Teacher.prototype = new Person();
var oT = new Teacher();
console.log( oT instanceof Teacher ); //true
console.log( oT instanceof Person ); //true
console.log( oT instanceof Object ); //true
console.log( Teacher.prototype.isPrototypeOf( oT ) ); //true
console.log( Person.prototype.isPrototypeOf( oT ) ); //true
console.log( Object.prototype.isPrototypeOf( oT ) ); //true
四,父类存在的方法和属性,子类可以覆盖(重写),子类没有的方法和属性,可以扩展
function Person() {}
Person.prototype.showUserName = function () {
console.log('Person::showUserName');
}
function Teacher() { }
Teacher.prototype = new Person();
Teacher.prototype.showUserName = function(){
console.log('Teacher::showUserName');
}
Teacher.prototype.showAge = function(){
console.log( 22 );
}
var oT = new Teacher();
oT.showUserName(); //Teacher::showUserName
oT.showAge(); //
五、重写原型对象之后,其实就是把构造函数的原型属性(prototype)的指向发生了改变
构造函数的原型属性(prototype)的指向发生了改变,会把原本的继承关系覆盖(切断)
function Person() {}
Person.prototype.showUserName = function () {
console.log('Person::showUserName');
}
function Teacher() {}
Teacher.prototype = new Person();
Teacher.prototype = {
showAge : function(){
console.log( 22 );
}
}
var oT = new Teacher();
oT.showAge(); //
oT.showUserName();
上例,第7行,Teacher.prototype重写了Teacher的原型对象(prototype),原来第6行的Teacher构造函数的prototype属性指向new Person()的关系切断了
所以在第14行,oT.showUserName() 就会发生调用错误,因为Teacher构造函数上的prototype属性不再指向父类(Person)的实例,继承关系被破坏了.
六、在继承过程中,小心处理实例的属性上引用类型的数据
function Person(){
this.skills = [ 'php', 'javascript' ];
}
function Teacher (){}
Teacher.prototype = new Person();
var oT1 = new Teacher();
var oT2 = new Teacher();
oT1.skills.push( 'linux' );
console.log( oT2.skills ); //php, java, linux
oT1的skills添加了一项linux数据,其他的实例都能访问到,因为其他实例中共享了skills数据,skills是一个引用类型
七、借用构造函数
为了消除引用类型影响不同的实例,可以借用构造函数,把引用类型的数据复制到每个对象上,就不会相互影响了
function Person( uName ){
this.skills = [ 'php', 'javascript' ];
this.userName = uName;
}
Person.prototype.showUserName = function(){
return this.userName;
}
function Teacher ( uName ){
Person.call( this, uName );
}
var oT1 = new Teacher();
oT1.skills.push( 'linux' );
var oT2 = new Teacher();
console.log( oT2.skills ); //php,javascript
console.log( oT2.showUserName() );
虽然oT1.skills添加了一项Linux,但是不会影响oT2.skills的数据,通过子类构造函数中call的方式,去借用父类的构造函数,把父类的属性复制过来,而且还能
传递参数,如第8行,但是第15行,方法调用错误,因为在构造中只复制了属性,不会复制到父类原型对象上的方法
八、组合继承(原型对象+借用构造函数)
经过以上的分析, 单一的原型继承的缺点有:
1、不能传递参数,如,
Teacher.prototype = new Person();
有些人说,小括号后面可以跟参数啊,没错,但是只要跟了参数,子类所有的实例属性,都是跟这个一样,说白了,还是传递不了参数
2、把引用类型放在原型对象上,会在不同实例上产生相互影响
单一的借用构造函数的缺点:
1、不能复制到父类的方法
刚好原型对象方式的缺点,借用构造函数可以弥补,借用构造函数的缺点,原型对象方式可以弥补,于是,就产生了一种组合继承方法:
function Person( uName ){
this.skills = [ 'php', 'javascript' ];
this.userName = uName;
}
Person.prototype.showUserName = function(){
return this.userName;
}
function Teacher ( uName ){
Person.call( this, uName );
}
Teacher.prototype = new Person();
var oT1 = new Teacher( 'ghostwu' );
oT1.skills.push( 'linux' );
var oT2 = new Teacher( 'ghostwu' );
console.log( oT2.skills ); //php,javascript
console.log( oT2.showUserName() ); //ghostwu
子类实例oT2的skills不会受到oT1的影响,子类的实例也能调用到父类的方法.
[js高手之路]从原型链开始图解继承到组合继承的产生的更多相关文章
- [js高手之路]使用原型对象(prototype)需要注意的地方
我们先来一个简单的构造函数+原型对象的小程序 function CreateObj( uName, uAge ) { this.userName = uName; this.userAge = uAg ...
- JS原型、原型链、构造函数、实例与继承
https://cloud.tencent.com/developer/article/1408283 https://cloud.tencent.com/developer/article/1195 ...
- [js高手之路]原型对象(prototype)与原型链相关属性与方法详解
一,instanceof: instanceof检测左侧的__proto__原型链上,是否存在右侧的prototype原型. 我在之前的两篇文章 [js高手之路]构造函数的基本特性与优缺点 [js高手 ...
- [js高手之路] 设计模式系列课程 - jQuery的extend插件机制
这里在之前的文章[js高手之路] 设计模式系列课程 - jQuery的链式调用与灵活的构造函数基础上增加一个extend浅拷贝,可以为对象方便的扩展属性和方法, jquery的插件扩展机制,大致就是这 ...
- [js高手之路]设计模式系列课程-组合模式+寄生组合继承实战新闻列表
所谓组合模式,就是把一堆结构分解出来,组成在一起,现实中很多这样的例子,如: 1.肯德基套餐就是一种组合模式, 比如鸡腿堡套餐,一般是是由一个鸡腿堡,一包薯条,一杯可乐等组成的 2.组装的台式机同理, ...
- [js高手之路] es6系列教程 - promise常见用法详解(resolve,reject,catch,then,all,race)
关于promise我在之前的文章已经应用过好几次,如[js高手之路]Node.js+jade+express+mongodb+mongoose+promise实现todolist,本文就来讲解下pro ...
- [js高手之路] javascript面向对象写法与应用
一.什么是对象? 对象是n个属性和方法组成的集合,如js内置的document, Date, Regexp, Math等等 document就是有很多的属性和方法, 如:getElementById, ...
- [js高手之路] es6系列教程 - 迭代器,生成器,for...of,entries,values,keys等详解
接着上文[js高手之路] es6系列教程 - 迭代器与生成器详解继续. 在es6中引入了一个新的循环结构for ....of, 主要是用来循环可迭代的对象,那么什么是可迭代的对象呢? 可迭代的对象一般 ...
- [js高手之路] es6系列教程 - 对象功能扩展详解
第一:字面量对象的方法,支持缩写形式 //es6之前,这么写 var User = { name : 'ghostwu', showName : function(){ return this.nam ...
随机推荐
- win7下elasticsearch5.0 安装head插件
项目开发用到了ES,5.X版本的,然而网上好多的安装资料都不能用,全是之前的老版本,今天弄了一上午终于完事了,总结一下安装的步骤. 1.安装NodeJs 去官网https://nodejs.org/e ...
- PHP ORM笔记
1.ORM是什么? 经常听到程序员的面试中会问到对ORM的了解,但是一直不知道ORM是个什么鬼东西,知道有一天在百度上顺带看到才发现ORM就是我们平时在框架中一直使用的数据库对象操作.ORM(Obje ...
- 使用boost/property_tree进行XML操作
之前一直用tinyxml来进行XML文件操作,刚刚接触的一个测试项目是使用boost操作的,虽然不清楚这两者的差异,但boost使用起来还挺方便的,所以简单整理一些关于boost解析和创建XML文件的 ...
- ReactiveSwift源码解析(八) SignalProducer的代码的基本实现
在前面几篇博客中我们详细的聊了ReactiveSwift中的Bag.Event.Observer以及Signal的使用方式和代码实现.那么在接下来的这几篇博客中,我们就依附于之前博客的基础上来聊一聊S ...
- 50行代码实现的高性能动画定时器 raf-interval
写在前面 raf-interval 是基于 window.requestAnimationFrame() 封装的定时器. Github: https://github.com/dntzhang/raf ...
- SparkMLlib-----GMM算法
Gaussian Mixture Model(GMM)是一个很流行的聚类算法.它与K-Means的很像,但是K-Means的计算结果是算出每个数据点所属的簇,而GMM是计算出这些数据点分配到各个类别的 ...
- docker~使用阿里加速器安centos
回到目录 上一篇说了hub.docker.com里拉个镜像太,而阿里云为我们做了不少本国镜像,这样下载的速度就很惊人了,下面看一下在centos7下配置阿里云加速器的方法 打开服务配置文件 vi /e ...
- Java开源博客My-Blog(SpringBoot+Docker)系列文章
My Blog 1.Docker+SpringBoot+Mybatis+thymeleaf的Java博客系统开源啦 2.My-Blog搭建过程:如何让一个网站从零到可以上线访问 3.将数据的初始化放到 ...
- 蓝桥杯比赛java 练习《立方变自身》
立方变自身 观察下面的现象,某个数字的立方,按位累加仍然等于自身.1^3 = 1 8^3 = 512 5+1+2=817^3 = 4913 4+9+1+3=17... 请你计算包括1,8, ...
- FasfDFS整合Java实现文件上传下载
文章目录 一 : 添加配置文件 二 : 加载配置文件 1. 测试加载配置文件 2. 输出配置文件 三:功能实现 1.初始化连接信 ...