`
WORM
  • 浏览: 19329 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JS继承方式

 
阅读更多

1、简介

      在JS中继承是一个非常复杂的话题,比其他任何面向对象语言中的继承都复杂得多。在大多数其他面向对象语言中,继承一个类只需使用一个关键字即可。在JS中想要达到继承公用成员的目的,需要采取一系列措施。JS属于原型式继承,得益于这种灵活性,我们既可以使用标准的基于类的继承,也可以使用更微妙一些的原型式继承。在JS中应该要明确一点,一切继承都是通过prototype来进行的,且JS是基于对象来继承的。


2、最直观的继承方式

      我们假设现在有这么一个继承层次:一个Person基类,含有name属性和一个getName方法;一个Author类,继承了Person类,有着特有的books属性和getBooks方法。最简单的类式继承方法如下:

[javascript] view plaincopy

    /*Class Person*/
    function Person(name){
        this.name = name;
    }
      
    Person.prototype.getName = function(){
            return this.name;
    };
      
    /*Class Author*/
    function Author(name, books){
        Person.call(this, name); //调用父类构造函数,第二次调用父类构造函数
        this.books = books;
    }
      
    Author.prototype = new Person(); //设置原型链以获得父类的方法, 第一次调用父类构造函数
    Author.prototype.constructor = Author; //设置构造函数
    Author.prototype.getBooks = function(){
        return this.books;
    };

上述代码的不足之处在于:实例化Authro对象时,将会2次调用父类的构造函数,且需要额外的保存原型链中实例化父类的对象,上述代码就保存了父类中的一份name属性,事实上这是不必要的。 如果继承层次多些,属性也多些,这个造成的负担就大了。 我们可以对此进行改进,事实上,我们需要的只是父类的原型上的东西,那些父类包含的属性通过调用父类构造函数已经获得了。另外,上述代码看起来似乎父类和子类的关联性不大,因此在这里,我们给函数原型增加一个extend函数,以明确表明继承层次,于是有了下面的原型式继承。


 3、原型式继承

       谈到原型式继承时,最好忘掉自己关于类和实例的一切知识,只从对象的角度来思考。 前面我们说到,JS是基于对象来继承的,对象的prototype属性实质也是指向一个实例化的对象。因此,我们可以想到,并不需要用类(构造函数)来定义对象的结构,只需直接创建一个对象即可。请看下面的代码:

[javascript] view plaincopy

    /*extend函数,实现原型式继承*/
    var extend = function(obj){
        if(typeof obj !== 'object'){
            throw new Error('fatal error: "Object.prototype.extend" expects a object');
        }
          
        var F = function(){}; //创建一个中间函数对象
        F.prototype = obj; //设置其原型对象指向obj
          
        return new F();//返回实例化的F
    };
      
    /*父类字面量对象*/
    var Person = {
        init: function(name){ //初始化函数,进行各种属性设置,代替了构造函数的作用
            this.name = name;
        },
        getName:function(){
            return this.name;
        }
    };
      
    /*子类对象*/
    var author1 = extend(Person); //继承Person
    author1.init('author');
    alert(author1.getName()); //输出author
    author1.books = 'xxx'; //设置自己的属性
    author1.getBooks = function(){
        return this.books;
    };
    alert(author1.getBooks());//输出xxx

上述代码通过全局的extend函数实现继承,接着根据需求进行成员的增加,父类的init函数充当了构造函数的角色,本质上,还是相当于将prototype指向一个实例化的Object对象。然而习惯了new操作符的同学可能会很不习惯这种方式的继承,包括我也很不习惯。为了使代码更符合我们的习惯,于是有了类式的继承。

4、类式继承

      类式继承则是模拟传统的面向对象语言实现的,通过给Function的prototype增加一个extend函数,子类构造函数通过extend函数继承父类,同时又通过内部我们设置的一个指向父类对象的引用,让我们可以很容易的直接调用父类对象的方法。具体如下:

[javascript] view plaincopy

    /*给函数原型增加一个extend函数,实现继承*/
    Function.prototype.extend = function(superClass){
        if(typeof superClass !== 'function'){
            throw new Error('fatal error:Function.prototype.extend expects a constructor of class');
        }
          
        var F = function(){}; //创建一个中间函数对象以获取父类的原型对象
        F.prototype = superClass.prototype; //设置原型对象
        this.prototype = new F(); //实例化F, 继承父类的原型中的属性和方法,而无需调用父类的构造函数实例化无关的父类成员
        this.prototype.constructor = this; //设置构造函数指向自己
        this.superClass = superClass; //同时,添加一个指向父类构造函数的引用,方便调用父类方法或者调用父类构造函数
          
        return this;
    };
          
          
    /*Class Person*/
    function Person(name){
        this.name = name;
    }
    Person.prototype.getName = function(){
            return this.name;
    };
      
      
    /*Class Author*/
    function Author(name, books){
        Author.superClass.call(this, name);
        this.books = books;
    }
    /*
     * 这里用了链式调用,下面语句等价于:
     * Author.extend(Person); Author.prototype.getBooks = function(){```};
     */
    Author.extend(Person).prototype.getBooks = function(){
        return this.books;
    };
    /*方法的覆写,通过superClass调用父类的方法获得基本信息,再调用子类的方法获得更特殊的信息*/
    Author.prototype.getName = function(){
        var name = Author.superClass.prototype.getName.call(this);
        return name + ', Author of ' + this.getBooks();
    };


上述代码的继承方式应该是现在各大框架和JS库最流行的继承方式了,一方面仅在实例化对象的时候调用了一次构造函数,且没有保存不必要的父类实例化的属性,通过superClass属性,又可以很轻松的调用父类的方法重写或增强子类的方法。

5、成员的扩充

      也许,有时候我们想给现有的对象扩充一组成员,于是我们可以把这组需要扩充的成员保存在一个对象中,然后遍历添加。于是我们扩展下extend函数,让其也可以接受类型为object的参数。

[javascript] view plaincopy

    Function.prototype.extend = function(superClass){
        if(typeof superClass === 'function'){//类式继承
            var F = function(){}; //创建一个中间函数对象以获取父类的原型对象
            F.prototype = superClass.prototype; //设置原型对象
            this.prototype = new F(); //实例化F, 继承父类的原型中的属性和方法,而无需调用父类的构造函数实例化无关的父类成员
            this.prototype.constructor = this; //设置构造函数指向自己
            this.superClass = superClass; //同时,添加一个指向父类构造函数的引用,方便调用父类方法或者调用父类构造函数
        } else if(typeof superClass === 'object'){ //方法的扩充
            var pro = this.prototype;
            for(var k in superClass){
                if(!pro[k]){ //如果原型对象不存在这个属性,则复制
                    pro[k] = superClass[k];
                }
            }
        } else {
            throw new Error('fatal error:"Function.prototype.extend" expects a function or object');
        }
          
        return this;
    };
    /*调用方式*/
    Author.extend({
        sayHello:function(){
            alert('Hello');
        }
    }).extend(classFunction.prototype);//原型也是对象,所以一样可以作为参数传入,这样便扩展了其他类的原型方法,有点类似多重继承。


上述代码中,我们扩展了extend函数,使其可以接受object类型的参数,以此实现成员的扩充,同时,若你不需要父类构造函数的调用,或者不想改变指向父类对象的引用,则可以通过传递新的构造函数的prototype即可。如果我们又有需求了,只需要扩展传递对象的部分方法,那么可以继续改造我们的extend函数,参数大于2的时候,后面的参数就是需要扩展的属性了。

       事实上,扩展后的extend函数也有些原型式继承的味道,只不过我们仍然需要定义子类的构造函数。从另一个角度看,扩展成员也有点多重继承的味道。

分享到:
评论

相关推荐

    6种JavaScript继承方式及优缺点(小结)

    温馨提示:想要更好的理解JS继承方式,须了解构造函数、原型对象、实例化对象、原型链等概念 第一种:原型链继承 利用原型链的特点进行继承 function Parent(){ this.name = 'web前端'; this.type = ['JS','...

    javascript 原生态js类继承实现的方式

    我们还知道,面向对象编程有三个重要的概念 - 封装、继承和多态。 但是在JavaScript的世界中,所有的这一切特性似乎都不存在。 因为JavaScript本身不是面向对象的语言,而是基于对象的语言。

    GolderBrother#learn-and-collect#JavaScript继承方式1

    组合继承使用原型链继承,并且利用借调父类的构造函数这两个方法的组合,可以实现一种组合式继承,方法通过父类的原型传给子类,然后子类调用父类的构造函数来获得父类的一

    JS继承的实现方式

    JS继承的实现方式转载整理。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

    JavaScript五种继承方式

    JavaScript继承第1种方式:对象冒充;第2种方式:call方法;第3种方式:apply方法;第4种方式:原型链方式;第5种方式:混合方式(推荐)

    js继承实现示例代码

    js继承实现示例代码,js继承实现示例代码,js继承实现示例代码

    JavaScript继承方式实例

    JavaScript继承方式实例,需要的朋友可以参考下。

    js实现的七种继承方式.md

    使用js实现继承的七种方式,详细讲解了js中的原型链继承,构造函数继承,组合继承(经典继承),原型式继承,寄生式继承,寄生组合式继承,以及ES6中的继承,描述原理以及实现和要点概述等。

    【JavaScript源代码】深入JS继承.docx

    深入JS继承  目录 前言准备总结继承的n种方式原型式继承原型链式继承借用构造函数(类式继承...撇开ES6 class不谈,传统的继承方式你知道几种?每种实现原理是什么,优劣点能谈谈吗。这里就结合具体例子,按照渐进式的

    JavaScript继承

    在JavaScript中继承是一个非常复杂的话题,比其他任何面向对象的语言中的继承都复杂得多。在大多数其他面向对象语言中,继承一个类只需使用一个关键字即可。与它们不同,在JavaScript中要想达到传承公用成员的目的,...

    js继承的实现

    js没有特别明确的实现如何继承,但是能够通过特殊手段实现继承的,有四种方法

    JavaScript的9种继承实现方式归纳

    不同于基于类的编程语言,如 C++ 和 Java,JavaScript 中的继承方式是基于原型的。同时由于 JavaScript 是一门非常灵活的语言,其实现继承的方式也非常多。 首要的基本概念是关于构造函数和原型链的,父对象的构造...

    【JavaScript的9种继承实现方式归纳】js实现继承的几种方式.pdf

    【JavaScript的9种继承实现方式归纳】js实现继承的几种方式.pdf

    js继承.doc

    javascript做为一门脚本语言,但面向对象思想在其中也有体现,本文档阐述和总结了js中继承的实现,及个方法的利弊!

    JS继承.txtJS继承.txt

    JS继承.txtJS继承.txtJS继承.txtJS继承.txtJS继承.txtJS继承.txtJS继承.txtJS继承.txtJS继承.txt

    Javascript继承机制原理

    Javascript继承机制原理 可以参考下

    js继承的用法

    js继承的用法, function initGrid(){ $('#'+instance.options.table).datagrid(instance.options); } initGrid(); return instance;

    Javascript编程中几种继承方式比较分析

    主要介绍了Javascript编程中几种继承方式比较分析,较为详细的分析了JavaScript继承的原理并对比分析了几种继承方式的实现技巧,需要的朋友可以参考下

Global site tag (gtag.js) - Google Analytics