JavaScript 继承

简介:

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

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

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

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

JavaScript 继承实现原理

根据duck typing的理论,在JavaScript中如果对象subType包含了superType的属性和方法,就可以认为其继承了superType。在JavaScript将原型链作为实现继承的主要方法。基本原理是利用原型让一个subType引用superType的属性和方法。如果对原型不是很了解可以看看JavaScript prototype,可以将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的继承,看个图

image

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

复制代码
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对象,这个继承也是通过原型链实现的。

image

所有函数都是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"]
复制代码

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

复制代码
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;
        }
复制代码

image

问题的根源就是调用两次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;
        }
复制代码

image

这样看起来不错了吧


    本文转自魏琼东博客园博客,原文链接:http://www.cnblogs.com/dolphinX/p/3307903.html,如需转载请自行联系原作者

相关文章
|
4天前
|
JavaScript 前端开发
Javascript如何实现继承?
【10月更文挑战第24天】JavaScript 中实现继承的方式有很多种,每种方式都有其优缺点和适用场景。在实际开发中,我们需要根据具体的需求和情况选择合适的继承方式,以实现代码的复用和扩展。
|
5月前
|
设计模式 JavaScript 前端开发
在JavaScript中,继承是一个重要的概念,它允许我们基于现有的类(或构造函数)创建新的类
【6月更文挑战第15天】JavaScript继承促进代码复用与扩展,创建类层次结构,但过深的继承链导致复杂性增加,紧密耦合增加维护成本,单继承限制灵活性,方法覆盖可能隐藏父类功能,且可能影响性能。设计时需谨慎权衡并考虑使用组合等替代方案。
46 7
|
5月前
|
JavaScript 前端开发
在 JavaScript 中,实现继承的方法有多种
【6月更文挑战第15天】JavaScript 继承常见方法包括:1) 原型链继承,利用原型查找,实例共享原型属性;2) 借用构造函数,避免共享,但方法不在原型上复用;3) 组合继承,结合两者优点,常用但有额外开销;4) ES6 的 class,语法糖,仍基于原型链,提供直观的面向对象编程。
35 7
|
2月前
|
自然语言处理 JavaScript 前端开发
一文梳理JavaScript中常见的七大继承方案
该文章系统地概述了JavaScript中七种常见的继承模式,包括原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承等,并探讨了每种模式的实现方式及其优缺点。
一文梳理JavaScript中常见的七大继承方案
|
2月前
|
JavaScript 前端开发
js之class继承|27
js之class继承|27
|
2月前
|
JSON JavaScript 前端开发
js原型继承|26
js原型继承|26
|
2月前
|
JavaScript 前端开发 开发者
JavaScript 类继承
JavaScript 类继承
18 1
|
2月前
|
JavaScript 前端开发
JS的几种继承方式
JavaScript中的几种继承方式视频。
13 0
|
3月前
|
开发者 图形学 iOS开发
掌握Unity的跨平台部署与发布秘籍,让你的游戏作品在多个平台上大放异彩——从基础设置到高级优化,深入解析一站式游戏开发解决方案的每一个细节,带你领略高效发布流程的魅力所在
【8月更文挑战第31天】跨平台游戏开发是当今游戏产业的热点,尤其在移动设备普及的背景下更为重要。作为领先的游戏开发引擎,Unity以其卓越的跨平台支持能力脱颖而出,能够将游戏轻松部署至iOS、Android、PC、Mac、Web及游戏主机等多个平台。本文通过杂文形式探讨Unity在各平台的部署与发布策略,并提供具体实例,涵盖项目设置、性能优化、打包流程及发布前准备等关键环节,助力开发者充分利用Unity的强大功能,实现多平台游戏开发。
91 0
|
3月前
|
JavaScript 前端开发 开发者
揭开JavaScript的神秘面纱:原型链背后隐藏的继承秘密
【8月更文挑战第23天】原型链是JavaScript面向对象编程的核心特性,它使对象能继承另一个对象的属性和方法。每个对象内部都有一个[[Prototype]]属性指向其原型对象,形成链式结构。访问对象属性时,若当前对象不存在该属性,则沿原型链向上查找。
31 0