[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 相关文章推荐
JQuery报错Uncaught TypeError: Illegal invocation的处理方法
Mar 13 Javascript
利用Node.JS实现邮件发送功能
Oct 21 Javascript
javascript ES6中箭头函数注意细节小结
Feb 17 Javascript
JavaScript中的this陷阱的最全收集并整理(没有之一)
Feb 21 Javascript
js中setTimeout的妙用--防止循环超时
Mar 06 Javascript
vue.js利用defineProperty实现数据的双向绑定
Apr 28 Javascript
了解ESlint和其相关操作小结
May 21 Javascript
JavaScript实现的鼠标跟随特效示例【2则实例】
Dec 22 Javascript
微信小程序生成分享海报方法(附带二维码生成)
Mar 29 Javascript
vue2.0+SVG实现音乐播放圆形进度条组件
Sep 21 Javascript
vue实现评价星星功能
Jun 30 Javascript
JavaScript parseInt0.0000005打印5原理解析
Jul 23 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的ob_start();控制您的浏览器cache!
2006/11/25 PHP
php中url传递中文字符,特殊危险字符的解决方法
2013/08/17 PHP
详解WordPress中过滤链接与过滤SQL语句的方法
2015/12/18 PHP
PHP实现Redis单据锁以及防止并发重复写入
2018/04/10 PHP
在vs2010中调试javascript代码方法
2011/02/11 Javascript
js获取当前路径的简单示例代码
2014/01/08 Javascript
IE8下Jquery获取select选中的值post到后台报错问题
2014/07/02 Javascript
什么是 AngularJS?AngularJS简介
2014/12/06 Javascript
js实现兼容性好的微软官网导航下拉菜单效果
2015/09/07 Javascript
详解JavaScript中this关键字的用法
2016/05/26 Javascript
JavaScript知识点总结(五)之Javascript中两个等于号(==)和三个等于号(===)的区别
2016/05/31 Javascript
JavaScript作用域示例详解
2016/07/07 Javascript
node.js程序作为服务并在windows下开机自启动(用forever)
2017/03/29 Javascript
微信小程序页面传值实例分析
2017/04/19 Javascript
微信小程序 action-sheet 反馈上拉菜单简单实例
2017/05/11 Javascript
Bootstrap table学习笔记(2) 前后端分页模糊查询
2017/05/18 Javascript
微信小程序 地图map实例详解
2017/06/07 Javascript
vue单页面实现当前页面刷新或跳转时提示保存
2018/11/02 Javascript
vue cli 3.x 项目部署到 github pages的方法
2019/04/17 Javascript
JavaScript实现轮播图效果代码实例
2019/09/28 Javascript
微信小程序开发中var that =this的用法详解
2020/01/18 Javascript
浅谈webpack构建工具配置和常用插件总结
2020/05/11 Javascript
python调用windows api锁定计算机示例
2014/04/17 Python
python获取本地计算机名字的方法
2015/04/29 Python
面向初学者的Python编辑器Mu
2018/10/08 Python
Python3之手动创建迭代器的实例代码
2019/05/22 Python
Django实现从数据库中获取到的数据转换为dict
2020/03/27 Python
python3.6.5基于kerberos认证的hive和hdfs连接调用方式
2020/06/06 Python
Python爬虫自动化获取华图和粉笔网站的错题(推荐)
2021/01/08 Python
英国第一的购买便宜玩具和游戏的在线购物网站:Bargain Max
2018/01/24 全球购物
社团招新策划书
2014/02/04 职场文书
英文求职信写作小建议
2014/02/16 职场文书
公务员学习中国梦心得体会
2016/01/05 职场文书
MySQL REVOKE实现删除用户权限
2021/06/18 MySQL
Java中常用解析工具jackson及fastjson的使用
2021/06/28 Java/Android
Python OpenCV之常用滤波器使用详解
2022/04/07 Python