资讯专栏INFORMATION COLUMN

类的继承

BothEyes1993 / 2393人阅读

摘要:一何为继承继承,是子类继承父类的特征和行为,使得子类对象具有父类的实例域和方法。目的是通过继承该父类,产出计算机子类。将父类的原型传递给子类使用操作符对父类进行实例化并将实例对象赋值给子类的。

本文讲述JavaScript中类继承的实现方式,并比较实现方式的差异。
一、何为继承
继承,是子类继承父类的特征和行为,使得子类对象具有父类的实例域和方法。
继承是面向对象编程中,不可或缺的一部分。
1.1 优点

减少代码冗余 父类可以为子类提供通用的属性,而不必因为增加功能,而逐个修改子类的属性

代码复用 同上

代码易于管理和扩展 子类在父类基础上,可以实现自己的独特功能

1.2 缺点

耦合度高 如果修改父类代码,将影响所有继承于它的子类

影响性能 子类继承于父类的数据成员,有些是没有使用价值的。但是,在实例化的时候,已经分配了内存。所以,在一定程度上影响程序性能。

二、例子

例子以图书馆中的书入库归类为例。
以下是简化后的父类Book(也可称为基类)。
目的是通过继承该父类,产出Computer(计算机)子类。
并且,子类拥有新方法say,输出自己的书名。

function Book(){
    this.name = ""; // 书名
    this.page = 0; // 页数
    this.classify = ""; // 类型
}
Book.prototype = {
    constructor: Book,
    init: function(option){
        this.name = option.name || "";
        this.page = option.page || 0;
        this.classify = option.classify || "";
    },
    getName: function(){
        console.log(this.name);
    },
    getPage: function(){
        console.log(this.page);
    },
    getClassify: function(){
        console.log(this.classify);
    }
};

接下来会讲解子类Computer几种继承方式的实现和优化方法。开始飙车~

三、实例式继承
function Computer(){
    Book.apply(this, arguments);
}
Computer.prototype = new Book();
Computer.prototype.constructor = Computer;
Computer.prototype.init = function(option){
    option.classify = "computer";
    Book.prototype.init.call(this, option);
};
Computer.prototype.say = function(){
    console.log("I"m "+ this.name);
}
3.1 调用父类构造器进行初始化
function Computer(){
    Book.apply(this, arguments);
}

Computer的构造函数里,调用父类的构造函数进行初始化操作。使子类拥有父类一样的初始化属性。

3.2 将父类的原型传递给子类

Computer.prototype = new Book();使用new操作符对父类Book进行实例化,并将实例对象赋值给子类的prototype
这样,子类Computer就可以通过原型链访问到父类的属性。

3.3 缺点

父类Book的构造函数被执行了2次

一次是在Computer的构造函数里Book.apply(this, arguments);

一次是在Computer.prototype = new Book();

这种模式,存在一定的性能浪费。

父类实例化无法传参

Computer.prototype = new Book();,这种实例化方式,无法让Book父类接收不固定的参数集合。

四、原型式继承
function Computer(){
    Book.apply(this, arguments);
}
Computer.prototype = Object.create(Book.prototype);
Computer.prototype.constructor = Computer;
Computer.prototype.init = function(option){
    option.classify = "computer";
    Book.prototype.init(option);
};
Computer.prototype.say = function(){
    console.log("I"m "+ this.name);
}

这里的改进:是使用Object.create(Book.prototype)。它的作用是返回一个继承自原型对象Book.prototype的新对象。且该对象下的属性已经初始化。
Object.create生成新对象,并不会调用到Book的构造函数。
这种方式,也可以通过原型链实现继承。

五、Object.create的简单版兼容

由于低版本的浏览器是不支持Object.create的。所以这里简单介绍下兼容版本:

Object.create = function(prototype){
    function F(){}
    F.prototype = prototype;
    return new F();
}

原理是定义一个空的构造函数,然后修改其原型,使之成为一个跳板,可以将原型链传递到真正的prototype。

六、函数化继承

上述两种实现方式,都存在一个问题:不存在私有属性私有方法。也就是说,存在被篡改的风险。
接下来就用函数化来化解这个问题。

function book(spec, my){
    var that = {};

    // 私有变量
    spec.name = spec.name || ""; // 书名
    spec.page = spec.page || 0; // 页数
    spec.classify = spec.classify || ""; // 类型

    var getName = function(){
        console.log(spec.name);
    };
    var getPage = function(){
        console.log(spec.page);
    };
    var getClassify = function(){
        console.log(spec.classify);
    };

    that.getName = getName;
    that.getPage = getPage;
    that.getClassify = getClassify;

    return that;
}

function computer(spec, my){
    spec = spec || {};
    spec.classify = "computer";
    var that = book(spec, my);

    var say = function(){
        console.log("I"m "+ spec.name);
    };
    that.say = say;

    return that;
}

var Ninja = computer({name: "JavaScript忍者秘籍", page: 350});

函数化的优势,就是可以更好地进行封装和信息隐藏。
也许有人疑惑为什么用以下这种方式声明和暴露方法:

var say = function(){
    console.log("I"m "+ spec.name);
};
that.say = say;

其实是为了保护对象自身的完整性。即使that.say被外部篡改或破坏掉,function computer内部的say方法仍然能够正常工作。
另外,解释下thatspecmy的作用:

that是一个公开数据存储容器,暴露出去的数据接口,都放到这个容器

spec是用来存储创建新实例所需的信息,属于实例之间共同编辑的数据

my是用来存储父类、子类之间共享的私密数据容器,外部是访问不到的。

七、ES6继承

最后,看下现代版ES6的类继承。不禁感慨以前的刀耕火种,是多么折磨人

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/96422.html

相关文章

  • C++继承

    摘要:例如,在关键字为的派生类当中,所继承的基类成员的访问方式变为。继承中的作用域在继承体系中的基类和派生类都有独立的作用域。为了避免类似问题,实际在继承体系当中最好不要定义同名的成员。 ...

    URLOS 评论0 收藏0
  • 关于继承的那些事!

    摘要:格式子类名父类名好处提高了代码的复用性提高了代码的维护性通过少量的修改,满足不断变化的具体要求让类与类产生了一个关系,是多态的前提要求有共同的属性或操作有细微的差别继承的弊端让类的耦合性增强。 showImg(https://segmentfault.com/img/remote/1460000019321816?w=600&h=242); 第二阶段 JAVA面向对象 第二章 继承 其...

    soasme 评论0 收藏0
  • JavaScript 的继承方式及优缺点

    摘要:继承简介在的中的面向对象编程,继承是给构造函数之间建立关系非常重要的方式,根据原型链的特点,其实继承就是更改原本默认的原型链,形成新的原型链的过程。 showImg(https://segmentfault.com/img/remote/1460000018998684); 阅读原文 前言 JavaScript 原本不是纯粹的 OOP 语言,因为在 ES5 规范中没有类的概念,在 ...

    nanchen2251 评论0 收藏0
  • Javascript 设计模式读书笔记(三)——继承

    摘要:的继承方式属于原型式继承,非常灵活。当使用关键字执行类的构造函数时,系统首先创建一个新对象,这个对象会继承自构造函数的原型对象新对象的原型就是构造函数的属性。也就是说,构造函数用来对生成的新对象进行一些处理,使这个新对象具有某些特定的属性。 继承这个东西在Javascript中尤其复杂,我掌握得也不好,找工作面试的时候在这个问题上栽过跟头。Javascript的继承方式属于原型式继承,...

    cangck_X 评论0 收藏0
  • C++基础语法(五)继承——万字总结,干货满满

    摘要:继承方式继承方式限定了基类成员在派生类中的访问权限,包括公有的私有的和受保护的。所以子类给父类引用赋值也是可以的,相当于给子类对象中继承的父类部分起了别名。如图成员函数也是如此,当子类与父类具有函数名相同的函数时,还是符合就近原则。 ...

    smartlion 评论0 收藏0
  • JavaScript面向对象编程-继承(三)

    摘要:子类不是父类实例的问题是由类式继承引起的。所以寄生式继承和构造函数继承的组合又称为一种新的继承方式。但是这里的寄生式继承处理的不是对象,而是类的原型。看上去略微复杂,还得好好研究。 寄生组合式继承(终极继承者) 前面学习了类式继承和构造函数继承组合使用,也就是组合继承,但是这种继承方式有个问题,就是子类不是父类的实例,而子类的原型是父类的实例。子类不是父类实例的问题是由类式继承引起的。...

    alaege 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<