博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript 继承
阅读量:6414 次
发布时间:2019-06-23

本文共 5306 字,大约阅读时间需要 17 分钟。

初学面向对象的时候总会背面向对象三个特征——封装、继承和多态,生怕考试或面试时自己答不上来。习惯于从C++、Java、C#的角度看待继承,工作后用到JavaScript觉得这种东西不会有继承,因为JavaScript没有强类型,怎么继承。

弱类型实现继承的理论可行性

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

基础弱类型语言多了就会渐渐听说鸭子类型(duck typing),在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定的,在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。通俗来说一个对象如果拥有某个对象的方法和属性,就可以看作是这个对象。

JavaScript 继承实现原理

根据duck typing的理论,在JavaScript中如果对象subType包含了superType的属性和方法,就可以认为其继承了superType。在JavaScript将原型链作为实现继承的主要方法。基本原理是利用原型让一个subType引用superType的属性和方法。如果对原型不是很了解可以看看,可以将subType的prototype属性设为superType的实例,这时候subType的原型链中就包含了superType的属性和方法,大致是这样的

function SuperType(){        this.property=true;        }    SuperType.prototype.getSuperValue=function(){        return this.property;        };    function SubType(){        this.subProperty=false;        }    SubType.prototype=new SuperType();    SubType.prototype.getSubValue=function(){        return this.subProperty;        }

这样SubType就实现了对SuperType的继承,看个图

验证一下,是不是这个样子

var instance=new SubType();    console.log(instance.getSuperValue()); //true    console.log(instance.getSubValue()); //false    console.log(instance instanceof SuperType); //true    console.log(instance instanceof SubType); //true

果不其然,SubType的实例同时也是SuperType的实例,可以访问到SuperType的属性和方法,根据duck typing这就实现了SubType对SuperType的继承。在上面的代码中SubType没有使用其默认的prototype对象,而是使用了SuperType的实例,这样SubType的实例拥有

1.SuperType实例的属性和方法

2.SuperType的prototype的属性和方法(如上图,在原型链中SuperType的实例有指向SuperType的prototype的指针)

3.SubType实例属性和方法(subProperty)

4.SubType新的prototype对象追加的属性和方法(getSubValue)

熟悉JavaScript的同学肯定清楚:和很多强类型面向对象语言一样,JavaScript的所有引用类型都默认继承与Object对象,这个继承也是通过原型链实现的。

所有函数都是Object对象的实例,这样是不是明白了为什么所有自定义类型都会用于toString, valueOf 等默认方法了吧,看起来很不错,但是这样的继承方式有两个需要注意的地方

1.给子类型的prototype对象内追加方法和属性必须在继承了父类型实例之后,否则追加的方法、属性会被重写,也就是说上面的例子

function SubType(){        this.subProperty=false;        }    SubType.prototype=new SuperType();    SubType.prototype.getSubValue=function(){        return this.subProperty;        }

不能写成这样

function SubType(){        this.subProperty=false;        }    SubType.prototype.getSubValue=function(){        return this.subProperty;        }        SubType.prototype=new SuperType();

2.子类型的prototype不能通过字面量赋值,这样也会重写原型链而不是追加,也就是说不能写成这样

function SubType(){        this.subProperty=false;        }    SubType.prototype=new SuperType();    SubType.prototype={        getSubValue:function(){            return this.subProperty;        }    }

组合继承

聪明的同学可能已经按捺不住要质问了,这样的做法当父类型中存在引用类型的属性的时候,子类型所有实例不是都共享这个属性了吗!

function SuperType(){        this.property=true;        this.colors=['red','blue','green'];        }    SuperType.prototype.getSuperValue=function(){        return this.property;        };    function SubType(){        this.subProperty=false;        }    SubType.prototype=new SuperType();    SubType.prototype.getSubValue=function(){        return this.subProperty;        }    var sub1=new SubType();    sub1.colors.push('yellow');    var sub2=new SubType();    console.log(sub2.colors);//["red", "blue", "green", "yellow"]

确实是这样的,但是这个错误很熟悉的赶脚有没有,在中也面临这个问题,使用构造函数可以使实例拥有自己独有的属性,问题就可以迎刃而解了

function SuperType(){        this.property=true;        this.colors=['red','blue','green'];        }    SuperType.prototype.getSuperValue=function(){        return this.property;        };    function SubType(){        this.subProperty=false;        SuperType.call(this);        }    SubType.prototype=new SuperType();     SubType.prototype.getSubValue=function(){        return this.subProperty;        }    var sub1=new SubType();    sub1.colors.push('yellow');    var sub2=new SubType();    console.log(sub2.colors);//["red", "blue", "green"]

这样在子类的构造函数中调用父类的构造函数,这样的写法实际上是在新创建的SubType的实例环境下调用SuperType的构造函数,实例中执行SuperType函数中对象初始化代码,Subtype的每个实例就会有自己的colors副本了。这样的继承方式在坊间被称为组合式继承。

这样做还多了个好处,可以传递参数了

function SuperType(name){        this.property=name;        this.colors=['red','blue','green'];        }    SuperType.prototype.getSuperValue=function(){        return this.property;        };    function SubType(name){        this.subProperty=false;        SuperType.call(this,name);        }    SubType.prototype=new SuperType();     SubType.prototype.getSubValue=function(){        return this.subProperty;        }    var sub1=new SubType('Byron');    console.log(sub1.property);//Byron

寄生组合式继承

看起来使用组合式继承很完美了,但是总是有但是,使用组合继承总会调用两次父类的构造函数,这样父类的实例属性和方法在子类的实例中会有两份。

function SubType(name){        this.subProperty=false;        SuperType.call(this,name); //第一次        }    SubType.prototype=new SuperType(); //第二次    SubType.prototype.getSubValue=function(){        return this.subProperty;        }

问题的根源就是调用两次SuperType的构造函数,其实在第一次调用的时候SubType就已经获得了SuperType的实例属性和方法,第二次调用的时候仅需要SuperType的prototype属性就可以了,因此可以这样写

function extend(subType,superType){    var _prototype=new Object(superType.prototype); //得到父类prototype对象副本    _prototype.constructor=subType; //constructor属性改为子类自己的    subType.prototype=_prototype; //重写子类prototype}    function SuperType(name){        this.property=name;        this.colors=['red','blue','green'];        }    SuperType.prototype.getSuperValue=function(){        return this.property;        };    function SubType(name){        this.subProperty=false;        SuperType.call(this,name);         }    extend(SubType,SuperType);    SubType.prototype.getSubValue=function(){        return this.subProperty;        }

这样看起来不错了吧

转载地址:http://hebra.baihongyu.com/

你可能感兴趣的文章
ES权威指南[官方文档学习笔记]-18 Distributed nature
查看>>
Xtrabackup--全量备份及恢复(二)
查看>>
Zipkin-1.19.0学习系列7:注解加载
查看>>
Apache Kafka源码剖析:第3篇 Acceptor&Processor细节
查看>>
java异常处理的throw和throws的区别
查看>>
iptables详细解释
查看>>
Linux文本处理工具grep
查看>>
JFinal + Jquery Mobile 日志记录webapp效果图
查看>>
更改掉nginx默认的用户和用户组
查看>>
微服务的「扩展立方」与 Docker 化实践
查看>>
linux 中逻辑卷的扩展和缩减及其快照卷的保存
查看>>
Mac下cocoapods使用说明(2016版)
查看>>
nodejs - json序列化&反序列化示例
查看>>
DPM 2012 R2恢复Exchange 2013单用户邮箱
查看>>
div+css总结—FF下div不设置高度背景颜色或外边框不能显示
查看>>
nfs挂载及优化
查看>>
MySQL读写分离--mysql-proxy和amoeba
查看>>
[Swift]UIKit学习之警告框:UIAlertController和UIAlertView
查看>>
linux运维实战练习-2015年9月5日课程作业
查看>>
我的友情链接
查看>>