JavaScript-继承

  1. 经典继承或伪造对象(借用构造函数)

思想:通过使用 apply() 和 call() 方法在(将来)新创建的对象上执行构造函数,从而解决原型中包含引用类型值所带来问题。

我们实际上是在(未来将要)新创建的 Student实例的环境下调用了 Person构造函数。这样一来,就会在新 Student对象上执行 Person() 函数中定义的所有对象初始化代码。结果,Student的每个实例就都会具有自己的name属性的副本了。

    function Person(){
        this.name=["zw","zz","ww"];
    }
   
    function Student(){
        //继承了Person
        Person.call(this);
    }
    
    var instance1=new Student();
    instance1.name.push("qq");
    console.log(instance1.name);//["zw", "zz", "ww", "qq"]
   
    var instance2=new Student();
    console.log(instance2.name);//["zw", "zz", "ww"]

借用构造函数可以在子类型构造函数中向超类型构造函数传递参数:

    function Person(name){
        this.name=name;
    }
   
    function Student(){
        //继承了Person
        Person.call(this,"zw");
        this.age=20;
    }
    
    var instance=new Student();
    console.log(instance.name);//zw
    console.log(instance.age);//20

但是借用构造函数有一个很大的缺点:就是解决不了函数复用。 

    2.组合继承(伪经典继承)

思路:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。

    function Person(sex) {
        this.sex = sex;
        this.name = ["zw", "zz", "ww"];
    }
    Person.prototype.getSex=function(){
        console.log(this.sex);
    };

    function Student(sex, age) {
        //继承属性
        Person.call(this,sex);
        this.age = age;
    }

    //继承方法
    Student.prototype=new Person();
    Student.prototype.constructor = Student;
    Student.prototype.getAge=function(){
        console.log(this.age);
    };

    var instance1 = new Student("女",20);
    instance1.name.push("qq");
    console.log(instance1.name); //["zw", "zz", "ww", "qq"]
    instance1.getSex();//女
    instance1.getAge();//20

    var instance2 = new Student("男",23);
    console.log(instance2.name);//["zw", "zz", "ww"]
    instance2.getSex();//男
    instance2.getAge();//23

        Person构造函数定义了两个属性:sex和name 。Person的原型定义了一个方法getSex。Student构造函数在调用Person狗咱函数是传入了sex参数,然后又定义了它自己的属性age。然后,将Person的实例复制给Student的原型,然后又在该新原型上定义了方法getAge()。这样一来,就可以让两个不同的student实例既分别拥有自己属性--包括name属性,又可以使用相同的方法了。

       组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为 JavaScript 中最常用的继
承模式。而且, instanceof 和 isPrototypeOf() 也能够用于识别基于组合继承创建的对象。

     3.原型式继承

思想:借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。

    object()函数

    function object(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }

在 object() 函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回了这个临时类型的一个新实例。 

    function object(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }
    var student = {
        name: "zw",
        course: ["软件工程", "计算机网络"]
    };

    var anotherStudent = object(student);
    anotherStudent.name = "zz";
    anotherStudent.course.push("JavaScript程序设计");
    console.log(anotherStudent.course);//["软件工程", "计算机网络", "JavaScript程序设计"]

    var otheranotherStudent = object(student);
    otheranotherStudent.name = "ww";
    otheranotherStudent.course.push("C语言程序设计");
    console.log(otheranotherStudent.course);//["软件工程", "计算机网络", "JavaScript程序设计", "C语言程序设计"]
    console.log(student.course);// ["软件工程", "计算机网络", "JavaScript程序设计", "C语言程序设计"]

 ES5中新增了Object.create()方法规范了原型式继承。这个方法接受两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,Object.create()与object方法的行为相同。

    var student = {
        name: "zw",
        course: ["软件工程", "计算机网络"]
    };

    var anotherStudent = Object.create(student);
    anotherStudent.name = "zz";
    anotherStudent.course.push("JavaScript程序设计");
    console.log(anotherStudent.course);//["软件工程", "计算机网络", "JavaScript程序设计"]

    var otheranotherStudent = Object.create(student);
    otheranotherStudent.name = "ww";
    otheranotherStudent.course.push("C语言程序设计");
    console.log(otheranotherStudent.course);//["软件工程", "计算机网络", "JavaScript程序设计", "C语言程序设计"]
   
    console.log(student.course);// ["软件工程", "计算机网络", "JavaScript程序设计", "C语言程序设计"]

 Object.create() 方法的第二个参数与 Object.defineProperties() 方法的第二个参数格式相同:每个属性都是通过自己的描述符定义的。以这种方式指定的任何属性都会覆盖原型对象上的同名属性。

    var student = {
        name: "zw",
        course: ["软件工程", "计算机网络"]
    };

    var anotherStudent = Object.create(student,{name:{value:"zz"}});
    console.log(anotherStudent.name);//zz

原型式继承中包含引用类型值的属性始终都会共享相应的值,就像使用原型模式一样。 

     4.寄生式继承

思路:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。

    function object(o) {
        function F() {}
        F.prototype = o;
        return new F();
    }

    function createAnother(original) {
        var clone = object(original);
        clone.sayHi = function () {
            console.log("hello");
        };
        return clone;
    }
    var student = {
        name: "zw",
        course: ["软件工程", "计算机网络"]
    };

    var anotherStudent = createAnother(student);
    anotherStudent.sayHi(); //hello
    console.log(anotherStudent.name); //zw

       这个例子中的代码基于student返回了一个新对象—— anotherStudent。新对象不仅具有student的所有属性和方法,而且还有自己的 sayHi() 方法。 

       使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一点与构造函数模式类似。

       5.寄生组合式继承

        组合继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。没错,子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。再来看一看下面组合继承的例子。

    function Person(sex) {
        this.sex = sex;
        this.name = ["zw", "zz", "ww"];
    }
    Person.prototype.getSex = function () {
        console.log(this.sex);
    };

    function Student(sex, age) {
        //继承属性
        Person.call(this, sex);//第二次调用Person()
        this.age = age;
    }

    //继承方法
    Student.prototype = new Person();//第一次调用Person()
    Student.prototype.constructor = Student;
    Student.prototype.getAge = function () {
        console.log(this.age);
    };

        在第一次调用Person构造函数时,Student.prototype会得到两个属性:name和sex;它们都是Person的实例属性,只不过现在位于Student的原型中。当调用Student构造函数时,又会调用一次Person构造函数,这一次有着新对象上创建了实例属性name和sex。于是,这两个属性就屏蔽了原型种的两个同名属性。

       寄生组合式继承思想:即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。

      寄生组合式继承的基本模式如下所示。

    function inheritPrototype(subType, superType) {
        var prototype = object(superType.prototype); //创建对象
        prototype.constructor = subType; //增强对象
        subType.prototype = prototype; //指定对象
    }

        这个示例中的 inheritPrototype() 函数实现了寄生组合式继承的最简单形式。这个函数接收两个参数:子类型构造函数和超类型构造函数。在函数内部,第一步是创建超类型原型的一个副本。第二步是为创建的副本添加 constructor 属性,从而弥补因重写原型而失去的默认的 constructor 属性。最后一步,将新创建的对象(即副本)赋值给子类型的原型。 

   function Person(sex) {
        this.sex = sex;
        this.name = ["zw", "zz", "ww"];
    }
    Person.prototype.getSex = function () {
        console.log(this.sex);
    };

    function Student(sex, age) {
        //继承属性
        Person.call(this, sex); 
        this.age = age;
    };

    //继承方法
    inheritPrototype(Student, Person);

    Student.prototype.getAge = function () {
        console.log(this.age);
    };

       这个例子的高效率体现在它只调用了一次Person构造函数,并且因此避免了在Student.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用instanceof 和 isPrototypeOf() 。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

                                                                                                                                                                  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值