js之继承

讲到继承,就先从创建对象讲起
我们都知道,以构造函数的方式创建对象,所创建的实例对象不能共享属性和方法
以原型链的方式创建对象,所创建的实例对象的属性和方法都是共享的,若某一实例对象修改属性值,会影响所有的实例对象
结合二者的优点与不足,js有一下几种创建对象的方式

创建对象

1.组合使用构造函数和原型

构造函数用于定义实例的属性,而原型模式用于定义方法和共享的属性

    function Person(name,age,job){
        this.name = name;
        this.age = age;
        this.job = job;
        this.friends = ["Shellby","Court"];
    }

    Person.prototype = {
        constructor:Person,
        sayName : function(){
            alert(this.name);
        }
    }

    var person1 = new Person("Nicholas",29,"Softwore Engineer");
    var person2 = new Person("Greg",30,"Doctor");

    person1.friends.push("Van");
    console.log(person1.friends);//["Shellby", "Court", "Van"]
    console.log(person2.friends);//["Shellby", "Court"]
    console.log(person1.friends === person2.friends) //false
    console.log(person1.sayName == person2.sayName)  //true

2.动态原型模式

动态原型模式把所有信息都封装在了构造函数中,而通过在构造函数中初始化原型,又保持了同时使用构造函数和原型的优点

    function Person(name,age,job){
        this.name = name;
        this.age = age;
        this.job = job;
    //方法
    if(typeof this.sayName != 'function'){
        Person.prototype.sayName = function(){
            alert(this.name);
        };
    }
}

注意if中的这段代码,这里只在sayName()方法不存在的情况下才会将它添加到原型中。if中的这段代码只会在初次调用构造函数时才会执行,此后,原型已经完成初始化,不需要再做什么修改。这里对原型所做的修改,能够立即在所有实例中得到反映。因此,这种方法可以说是 很完美!

3.寄生构造函数模式

寄生构造函数模式的思想是创建一个函数,该函数的作用不仅仅是封装创建对象的代码,然后再返回新创建的对象

    function Person(name,age,job){
        var o = new Object();
        o.name = name;
        o.age = age;
        o.job = o.job;
        o.sayName = function(){
            alert(this.name);
        };
        return o;
    }
    var friend = new Person("Mary",23,"Teacher");
    friend.sayName(); //Mary
    console.log(friend instanceof Person);//false
    console.log(friend instanceof Object);//true

这个模式可以在特殊的情况下用来为对象创建构造函数。假设我们想创建一个具有额外方法的特殊数组。由于不能直接修改Array构造函数,因此可以使用这个模式。

    function SpecialArray(){
        //创建数组
        var values = new Array();
        //添加值
        values.push.apply(values,arguments);
        //添加方法
        values.toPipedString = function(){
            return this.join("|");
        };
        //返回数组
        return values;
    }

    var colors = new SpecialArray("red","blue","green");
    alert(colors.toPipedString()); // red|blue|green

4.稳妥式构造函数模式

所谓稳妥对象,指的是没有公共属性,而且方法也不引用this对象。 稳妥构造函数遵循与寄生函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用this;二是不使用new操作符调用构造函数

    function Person(name,age,job){
        //创建要返回的对象
        var o = new Object();
        //在这里可以定义私有变量和函数
        // 添加方法
        o.sayName = function(){
            alert(name);
        }
        //返回对象
        return o;
    }
    var friend = Person("Nicholas",29,"Softwore Engineer");
    friend.sayName(); //Nicholas

继承

1.原型式继承

核心:将父类的实例作为子类的原型

    function SuperType(){
        this.colors = ["red","blue","green"];
    }
    SuperType.prototype.sayColors = function(){
        console.log(this.colors);
    }
    function SubType(){
    }
    SubType.prototype = new SuperType();
    var instance1 = new SubType();
    instance1.colors.push("black");
    alert(instance1.colors);//red,blue,green,black
    console.log(instance1.sayColors()); //["red", "blue", "green", "black"]
    console.log(instance1 instanceof SuperType);//true
    console.log(instance1 instanceof SubType);//true

    var instance2 = new SubType();
    alert(instance2.colors);//red,blue,green,black

特点:
- 非常纯粹的继承关系,实例是子类的实例,也是父类的实例
- 父类新增原型方法/原型属性,子类都能访问到
- 简单,易于实现
缺点:
- 可以在Cat构造函数中,为Cat实例增加实例属性。如果要新增原型属性和方法,则必须放在new Animal()这样的语句之后执行。
- 无法实现多继承
- 来自原型对象的引用属性是所有实例共享的(详细请看附录代码: 示例1)
- 创建子类实例时,无法向父类构造函数传参

推荐指数:★★(3、4两大致命缺陷)

2.借用构造函数继承

核心:使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
通过使用call()和apply()

    function SuperType(){
        this.name = 'SuperType';
        this.colors = ["red","blue","green"];
        this.sayHi = function(){
            console.log("Hi!"+this.name);
        }
    }
    SuperType.prototype.sayColors = function(){
        console.log(this.colors);
    }
    function SubType(){
        //继承了SuperType
        SuperType.call(this);
    }
    var instance1 = new SubType();
    instance1.colors.push("black");
    alert(instance1.colors);//red,blue,green,black
    console.log(instance1 instanceof SuperType);//false
    console.log(instance1 instanceof SubType);//true
    console.log(instance1.sayHi());//Hi!SuperType
    console.log(instance1.sayColors());//报错

    var instance2 = new SubType();
    alert(instance2.colors);//red,blue,green

传递参数

    function SuperType(name){
        this.name = name;
    }
    function SubType(){
        //继承了SuperType,同时还传递了参数
        SuperType.call(this,"Jerry");
        //实例属性
        this.age = 24;
    }
    var instance1 = new SubType();
    alert(instance1.name); //Jerry
    alert(instance1.age);  //24

特点:

  • 解决了1中,子类实例共享父类引用属性的问题
  • 创建子类实例时,可以向父类传递参数
  • 可以实现多继承(call多个父类对象)
    缺点:

  • 实例并不是父类的实例,只是子类的实例

  • 只能继承父类的实例属性和方法,不能继承原型属性/方法
  • 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
    推荐指数:★★(缺点3)

3.组合继承

组合继承也叫伪经典继承。指的是将原型链和借用构造函数的技术组合到一起,从而发挥而知之长的一种继承模式
核心:通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

    function SuperType(name){
        this.name = name||'SuperType';
        this.colors = ["red","blue","green"];
        this.sayName = function(){
            console.log("Hi! "+this.name);
        }
    }
    SuperType.prototype.sayColors = function(){
        console.log(this.colors);
    };
    function SubType(name,age){
        //调用父类构造
        SuperType.call(this,name);
        this.age = age;
    }
    //将父类实例作为子类原型
    SubType.prototype = new SuperType();
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge = function(){
        console.log(this.age);
    };

    var instance1 = new SubType("Nicholas",29);
    instance1.colors.push("black");
    console.log(instance1.colors);//(4) ["red", "blue", "green", "black"]
    instance1.sayName();//Hi! Nicholas
    instance1.sayAge();//29

    console.log(instance1 instanceof SubType); //true
    console.log(instance1 instanceof SuperType);//true

    var instance2 = new SubType("Jane",23);
    console.log(instance2.colors);//(4) ["red", "blue", "green"]
    instance2.sayName();//Hi! Jane
    instance2.sayAge();//23

特点:

  • 弥补了方式2的缺陷,可以继承实例属性/方法,也可以继承原型属性/方法
  • 既是子类的实例,也是父类的实例
  • 不存在引用属性共享问题
  • 可传参
  • 函数可复用
    缺点:

  • 调用了两次父类构造函数,生成了两份实例(子类实例将子类原型上的那份屏蔽了)
    推荐指数:★★★★(仅仅多消耗了一点内存)

4.寄生式继承

核心:为父类实例添加新特性,作为子类实例返回

    function SuperType(name){
        this.name = name||'SuperType';
        this.colors = ["red","blue","green"];
        this.sayName = function(){
            console.log("Hi! "+this.name);
        }
    }
    SuperType.prototype.sayColors = function(){
        console.log(this.colors);
    };

    function SubType(name,age){
        //创建父类实例,并添加新特性
        var superType = new SuperType();
        superType.name = name || 'subtype';
        superType.age = age;
        return superType;
    }
    var instance1 = new SubType("Lara",23);
    instance1.colors.push("pink");
    console.log(instance1.colors);//["red", "blue", "green", "pink"]
    console.log(instance1.name); //Lara
    console.log(instance1.age); //23
    console.log(instance1.sayName()); //Hi! Lara
    console.log(instance1.sayColors()); // ["red", "blue", "green", "pink"]

    console.log(instance1 instanceof SuperType);//true
    console.log(instance1 instanceof SubType);//false

    var instance2 = new SubType("Daisy",24);
    console.log( instance2.colors); //["red", "blue", "green"]
    console.log(instance1.name); //Daisy
    console.log(instance1.age); //24

特点:

  • 不限制调用方式,不管是new 子类()还是子类(),返回的对象具有相同的效果
    缺点:

  • 实例是父类的实例,不是子类的实例

  • 不支持多继承
    推荐指数:★★

5.寄生组合式继承

暂时没有理解透彻

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值