• 浅谈js中继承的理解和实现


    一、前言

    java、C#等正统面向对象语言都会提供类似extend之类的处理类的继承的方法,而javascript并没有提供专门的方法用于继承,在javascript中使用继承需要一点技巧。js中实例的属性和行为是由构造函数和原型两部分组成的,js的继承也分为这两部分。下面给大家分享一下在js中如何实现继承,讲的不对的地方望大家指正!

    二、继承构造函数中的属性和行为

    我们定义两个类Animal和Bird类,来实现js中类的继承。

    //定义Animal类
                function Animal(name){
                    this.name = name;
                    this.type = 'animal';
                }
                //Animal原型添加say方法
                Animal.prototype.say = function(){
                    alert("I'm a(an) "+ this.type +", my name is "+ this.name);
                };

    接下来定义Bird类

    //定义Bird类
    function Bird(name){
        this.name = name;
        this.type = 'bird';
    }
    
    Bird.prototype = {
    
    };

    我们先让Bird类继承Animal的构造函数中的属性和行为,代码清单如下

    //定义Animal类
    function Animal(name){
        this.name = name;
        this.type = 'animal';
    }
    //Animal原型添加say方法
    Animal.prototype = {
        say : function(){
            alert("I'm a(an) "+ this.type +", my name is "+ this.name);
        }
    };
    
    //定义Bird类
    function Bird(name){
        this.name = name;
        this.type = 'animal';
    }
    
    Bird.prototype = {
    
    };
    
    //实例化Bird类
    var canary = new Bird('yinwu');
    alert(canary.type);

    程序运行正常,但看不出继承,因为在Bird类的构造函数中是将Animal的属性和行为复制了一份。同时,缺点也很明显,如果Animal类的构造函数有任何改动,Bird类也要手动的同步改动,同样一份代码,复制在程序中不同地方,违反了DRY原则,降低了代码的可维护性。需要改进,改进后的代码清单如下:

    //定义Animal类
    function Animal(name){
        this.name = name;
        this.type = 'animal';
    }
    //Animal原型添加say方法
    Animal.prototype = {
        say : function(){
            alert("I'm a(an) "+ this.type +", my name is "+ this.name);
        }
    };
    
    //定义Bird类
    function Bird(name){
        Animal.call(this, name);
    }
    
    Bird.prototype = {
    
    };
    
    //实例化Bird类
    var canary = new Bird('yinwu');
    alert(canary.type);//animal

    我们把Bird类改为了Animal.call(this, name);,也就是通过call()方法实现让Animal内部的this指向Bird类的实例。改进后的代码就实现了构造函数的属性和行为的继承,最后alert(canary.type)弹出的信息为animal。

    三、原型的继承

    由继承我们知道,继承后子类拥有了父类同样的属性和行为,那么能否将Animal类的原型直接传给Bird类的原型呢?请看如下代码清单:

    //定义Animal类
    function Animal(name){
        this.name = name;
        this.type = 'animal';
    }
    //Animal原型添加say方法
    Animal.prototype = {
        say : function(){
            alert("I'm a(an) "+ this.type +", my name is "+ this.name);
        }
    };
    
    //定义Bird类
    function Bird(name){
        Animal.call(this, name);
    }
    
    Bird.prototype = Animal.prototype;
    
    //实例化Bird类
    var canary = new Bird('yinwu');
    canary.say();//I'm a(an) animal, my name is yinwu

    通过把Animal类的原型直接传给Bird类的原型,Bird类拥有了Animal原型中的say行为。如果给Bird类的原型添加fly行为,结果会如何呢?如代码清单:

    //定义Animal类
    function Animal(name){
        this.name = name;
        this.type = 'animal';
    }
    //Animal原型添加say方法
    Animal.prototype = {
        say : function(){
            alert("I'm a(an) "+ this.type +", my name is "+ this.name);
        }
    };
    
    //定义Bird类
    function Bird(name){
        Animal.call(this, name);
    }
    
    Bird.prototype = Animal.prototype;
    //Bird类原型添加fly方法
    Bird.prototype.fly = function(){
        alert("I'm flying more high ");
    }
    //实例化Bird类
    var canary = new Bird('yinwu');
    canary.say();//I'm a(an) animal, my name is yinwu
    canary.fly();//I'm flying more high
    var pig = new Animal('xiaozhu');
    pig.fly();//I'm flying more high

    程序运行你会发现Animal类也获得了fly行为,pig对象调用fly行为弹出I'm flying more high的提示信息,我们只给Bird类添加fly行为,为什么Animal类也获得了fly行为呢?原因在于”Bird.prototype = Animal.prototype;”这句代码,因为prototype原型本质上是hash对象,赋值时会进行传址,也就是说Bird.prototype原型和Animal.prototype原型指向同一内存地址,对Bird.prototype原型添加fly行为也就相当于为Animal.prototype原型添加了fly行为,这就是pig会fly的原因。如何对代码进行改进呢?请看如下代码清单:

    //定义Animal类
    function Animal(name){
        this.name = name;
        this.type = 'animal';
    }
    //Animal原型添加say方法
    Animal.prototype = {
        say : function(){
            alert("I'm a(an) "+ this.type +", my name is "+ this.name);
        }
    };
    
    //定义Bird类
    function Bird(name){
        Animal.call(this, name);
    }
    
    Bird.prototype = new Animal();
    Bird.prototype.constructor = Bird;
    //Bird类原型添加fly方法
    Bird.prototype.fly = function(){
        alert("I'm flying more high ");
    }
    //实例化Bird类
    var canary = new Bird('yinwu');
    canary.say();//I'm a(an) animal, my name is yinwu
    canary.fly();//I'm flying more high
    var pig = new Animal('xiaozhu');
    pig.fly();//报错

    与前一代码清单相比,把”Bird.prototype = Animal.prototype;”这句代码,改成了”Bird.prototype = new Animal(); Bird.prototype.constructor = Bird;”,这两句什么意思呢? ”Bird.prototype = new Animal();”意思是Bird.prototype原型作为Animal类的实例,那么Bird原型对象中包含了一个指向Animal原型对象的指针;“Bird.prototype.constructor = Bird;”意思是因为“Bird.prototype = new Animal();”时,Bird.prototype.constructor指向了Animal原型对象,将其纠正重新指向Bird。代码运行后你会发现pig.fly()会报错,此fly()行为undefined,那是因为Animal原型中没有fly行为。到此我们就实现了Bird类对Animal类的原型继承。

    四、js继承的封装

    前面二、三小节中分别实现了构造函数和原型的属性和行为的继承,这节中我们将js中实现继承的过程进行封装,向外提供接口调用。请看下列代码:

    //js继承封装成函数并提供接口
    function extend(subClass, superClass){
        var F = function(){};
        F.prototype = superClass.prototype;//F.prototype 原型对象和superClass.prototype原型对象指向同一内存地址
        subClass.prototype = new F();//subClass.prototype作为F类的实例
        subClass.prototype.constructor = subClass;//将subClass.prototype.constructor重新指向subClass
        subClass.superclass = superClass.prototype;//subClass类中定义superclass属性使其指向superClass.prototype
        if(superClass.prototype.constructor == Object.prototype.constructor){
            superClass.prototype.constructor = superClass;
        }
    }
    
    //定义Animal类
    function Animal(name){
        this.name = name;
        this.type = 'animal';
    }
    //Animal原型添加say方法
    Animal.prototype.say = function(){
        alert("I'm a(an) "+ this.type +", my name is "+ this.name);
    };
    
    //定义Bird类
    function Bird(name){
        this.constructor.superclass.constructor.apply(this, arguments);//通过apply方法继承Animal类中的属性和行为
        this.type = 'bird';
    }
    
    //实现对Animal继承
    extend(Bird, Animal);
    
    Bird.prototype.fly = function(){
        alert("I'm flying more high ");
    };
    
    //实例化
    var canary = new Bird('yinwu');
    canary.say();//I'm a(an) animal, my name is yinwu
    canary.fly();//I'm flying more high

    通过上述方法将js中的继承封装在函数中,要用到继承时直接调用即可。

    此文章发布在本人博客园changjianqiu,如需转载本文,请注明来源: http://www.cnblogs.com/changjianqiu/

  • 相关阅读:
    android ListView布局之一(继承listActivity、使用arrayAdapter)
    android your project contains error
    wojilu系统的ORM代码解析[源代码结构分析,ObjectBase基类分析]
    ORM中启用数据库事务
    我记录网站综合系统 技术原理解析[11:ActionProcessor流程wojilu核心]
    互联网,让我们更安全了,还是更危险了【纯讨论】
    不用服务器也能跑的框架wojilu续篇
    使用wojilu 无代码实现 输入框提示 及其背后的原理
    wojilu日志系统可以单独使用
    “我有什么” 和 “你要什么” 框架制作的一些思考
  • 原文地址:https://www.cnblogs.com/changjianqiu/p/4202785.html
Copyright © 2020-2023  润新知