[js高手之路]从原型链开始图解继承到组合继承的产生详解


Posted in Javascript onAugust 28, 2017

于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高手之路]从原型链开始图解继承到组合继承的产生详解

如果你不会画这个图,你需要去看下我的这篇文章:

第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的隐式原型(__proto__)只指向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(); //22

五、重写原型对象之后,其实就是把原型对象的__proto__的指向发生了改变

原型对象prototype的__proto__的指向发生了改变,会把原本的继承关系覆盖(切断)

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(); //22
 oT.showUserName();

上例,第7行,Teacher.prototype重写了Teacher的原型对象(prototype),原来第6行的原型对象的隐式原型(__proto__)指向就没有作用了

所以在第14行,oT.showUserName() 就会发生调用错误,因为Teacher的原型对象(prototype)的隐式原型(__proto__)不再指向父类(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高手之路]从原型链开始图解继承到组合继承的产生详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript RegExp方法获取地址栏参数(面向对象)
Mar 10 Javascript
ExtJS GTGrid 简单用户管理
Jul 01 Javascript
javascript 事件处理、鼠标拖动效果实现方法详解
May 11 Javascript
JQuery动画与特效实例分析
Feb 02 Javascript
JavaScript框架是什么?怎样才能叫做框架?
Jul 01 Javascript
jQuery mobile转换url地址及获取url中目录部分的方法
Dec 04 Javascript
Node.js读取文件内容示例
Mar 07 Javascript
利用jquery正则表达式在页面验证url网址输入是否正确
Apr 04 jQuery
ES6新特性六:promise对象实例详解
Apr 21 Javascript
详解基于webpack2.x的vue2.x的多页面站点
Aug 21 Javascript
vue-cli 打包后提交到线上出现 "Uncaught SyntaxError:Unexpected token" 报错
Nov 06 Javascript
JS实现可视化音频效果的实例代码
Jan 16 Javascript
jQuery菜单实例(全选,反选,取消)
Aug 28 #jQuery
[js高手之路]原型式继承与寄生式继承详解
Aug 28 #Javascript
[js高手之路]寄生组合式继承的优势详解
Aug 28 #Javascript
ajax+node+request爬取网络图片的实例(宅男福利)
Aug 28 #Javascript
js排序与重组的实例讲解
Aug 28 #Javascript
利用ES6的Promise.all实现至少请求多长时间的实例
Aug 28 #Javascript
JS自定义函数实现时间戳转换成date的方法示例
Aug 27 #Javascript
You might like
PHP的FTP学习(二)[转自奥索]
2006/10/09 PHP
用PHP书写安全的脚本代码
2012/02/05 PHP
PHP+memcache实现消息队列案例分享
2014/05/21 PHP
PHP环境中Memcache的安装和使用
2015/11/05 PHP
ThinkPHP删除栏目(实现批量删除栏目)
2017/06/21 PHP
PHP实现简单的模板引擎功能示例
2017/09/02 PHP
Javascript引用指针使用介绍
2012/11/07 Javascript
给事件响应函数传参数的四种方式小结
2013/12/05 Javascript
如何设置一定时间内只能发送一次请求
2014/02/28 Javascript
JS上传组件FileUpload自定义模板的使用方法
2016/05/10 Javascript
JS获取复选框的值,并传递到后台的实现方法
2016/05/30 Javascript
JavaScript数据结构之优先队列与循环队列实例详解
2017/10/27 Javascript
使用axios实现上传图片进度条功能
2017/12/21 Javascript
vue2.0 element-ui中el-select选择器无法显示选中的内容(解决方法)
2018/08/24 Javascript
vue观察模式浅析
2018/09/25 Javascript
jQuery cookie的公共方法封装和使用示例
2020/06/01 jQuery
理解Proxy及使用Proxy实现vue数据双向绑定操作
2020/07/18 Javascript
windows 10下安装搭建django1.10.3和Apache2.4的方法
2017/04/05 Python
Python学习笔记之open()函数打开文件路径报错问题
2018/04/28 Python
python3的输入方式及多组输入方法
2018/10/17 Python
Python绘制并保存指定大小图像的方法
2019/01/10 Python
Python 多线程搜索txt文件的内容,并写入搜到的内容(Lock)方法
2019/08/23 Python
ubuntu上安装python的实例方法
2019/09/30 Python
flask 实现token机制的示例代码
2019/11/07 Python
如何在scrapy中集成selenium爬取网页的方法
2020/11/18 Python
利用CSS3的transform做的动态时钟效果
2011/09/21 HTML / CSS
基于CSS3实现图片模糊过滤效果
2015/11/19 HTML / CSS
美国成衣女装品牌:CHICO’S
2016/09/19 全球购物
联想瑞士官方网站:Lenovo Switzerland
2017/11/19 全球购物
Kidsroom台湾:来自德国的婴儿用品
2017/12/11 全球购物
巴西美妆购物网站:Kutiz Beauté
2019/03/13 全球购物
美国电子产品购物网站:BuyDig.com
2020/06/17 全球购物
销售副总经理岗位职责
2013/12/11 职场文书
浪漫婚礼主持词
2014/03/14 职场文书
2014年公务员退休工资改革方案
2014/10/01 职场文书
python数字图像处理之图像的批量处理
2022/06/28 Python