美文网首页
JavaScript继承(原型链继承、构造函数继承、组合继承)

JavaScript继承(原型链继承、构造函数继承、组合继承)

作者: 谢_ffd1 | 来源:发表于2021-03-30 18:08 被阅读0次

    原型链继承

    将父类的实例作为子类的原型

    // Shape - 父类(superclass)
    function Shape() {
      this.name = "123";
      this.color = [1,2,3];
    }
    // 父类的方法
    Shape.prototype.getName= function() {
      console.log(this.name);
    };
    // Rectangle - 子类(subclass)
    function Rectangle() {
    }
    Rectangle.prototype = new Shape();
    var child = new Rectangle();
    child.getName();
    

    缺点:引用类型的属性被所有实例共享

    // Shape - 父类(superclass)
    function Shape() {
      this.name = "123";
      this.color = [1,2,3];
    }
    // 父类的方法
    Shape.prototype.getName= function() {
      console.log(this.name);
    };
    // Rectangle - 子类(subclass)
    function Rectangle() {
    }
    Rectangle.prototype = new Shape();
    var child = new Rectangle();
    var child2 = new Rectangle();
    child.getName();
    child.color.push('yellow');
    console.log(child.color);  //打印输出 [1, 2, 3, "yellow"]
    console.log(child2.color); //打印输出 [1, 2, 3, "yellow"]
    

    缺点2:无法在不影响其它实例的前提下向父类传递参数

     // Shape - 父类(superclass)
        function Shape(color) {
            this.name = "123";
            this.color = color;
        }
        // 父类的方法
        Shape.prototype.getName= function() {
            console.log(this.name);
        };
        // Rectangle - 子类(subclass)
        function Rectangle() {
    
        }
        Rectangle.prototype = new Shape([4,5,6])
        var child = new Rectangle();
        var child2 = new Rectangle();
        console.log(child.color);
        console.log(child2.__proto__.color);
    

    构造函数继承

    复制父类构造函数内的属性

    // Shape - 父类(superclass)
        function Shape(age) {
            this.name = "123";
            this.color = [1,2,3];
            this.age = age;
            this.setName = function () {
                this.name = "456";
            }
        }
        // 父类的方法
        Shape.prototype.getName= function() {
            console.log(this.name);
        };
        // Rectangle - 子类(subclass)
        function Rectangle(age) {
            //向父类传递age参数
            Shape.call(this,age)  //或使用  Shape.apply(this) 差别在于传参方式不同
        }
    
        var child = new Rectangle(10);
        var child2 = new Rectangle(18);
        //避免了引用类型的属性被所有实例共享
        child.color.push('yellow');
        console.log(child.color);  //输出打印 [1, 2, 3, "yellow"]
        console.log(child2.color); //输出打印 [1, 2, 3]
        //只能继承父类的实例属性和方法,
        child.setName();
        console.log(child.name);  //输出打印 456
        console.log(child2.name); //输出打印 123
        
        //无法实现复用,每个子类都有父类实例函数的副本,影响性能
        console.log(child);
        console.log(child2);
        child.getName(); //不能继承原型属性/方法 报错
    

    优点:1.避免了引用类型的属性被所有实例共享 2.可以向父类传递参数
    缺点:1.只能继承父类的实例属性和方法,不能继承原型属性/方法 2.无法实现复用,每个子类都有父类实例函数的副本,影响性能


    image.png

    组合继承

    结合原型链继承和构造函数继承通过调用父类构造,继承父类的属性并保留传参的优点,然后通过将父类实例作为子类原型,实现函数复用

     // Shape - 父类(superclass)
        function Shape(age) {
            this.name = "123";
            this.color = [1,2,3];
            this.age = age;
        }
        // 父类的方法
        Shape.prototype.getName= function() {
            console.log(this.name);
        };
        // Rectangle - 子类(subclass)
        function Rectangle(age) {
            //向父类传递age参数
            Shape.call(this,age)  //或使用  Shape.apply(this) 差别在于传参方式不同
        }
        Rectangle.prototype = new Shape();
        var child = new Rectangle(10);
        var child2 = new Rectangle(18);
        child.color.push('yellow');
        console.log(child.color);  //输出打印 [1, 2, 3, "yellow"]
        console.log(child2.color); //输出打印 [1, 2, 3]
        console.log(child);
        console.log(child2);
        child.getName();
    

    优点:融合原型链继承和构造函数的优点,是JavaScript中最常用的继承模式
    缺点:调用了两次父类构造函数,父类中的实例属性和方法既存在于子类的实例中,又存在于子类的原型中,不过仅是内存占用,因此,在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法


    image.png

    组合继承(优化1)

    让子类原型引用父类原型(剔除已有的父类的实例属性和方法,只要父类的原型属性/方法,同时减少了一次父类函数调用)

     // Shape - 父类(superclass)
        function Shape(age) {
            this.name = "123";
            this.color = [1,2,3];
            this.age = age;
        }
        // 父类的方法
        Shape.prototype.getName= function() {
            console.log(this.name);
        };
        // Rectangle - 子类(subclass)
        function Rectangle(age) {
            //向父类传递age参数
            Shape.call(this,age)  //或使用  Shape.apply(this) 差别在于传参方式不同
        }
        Rectangle.prototype = Shape.prototype;  //这里让子类原型引用父类原型
        var child = new Rectangle(10);
        var child2 = new Rectangle(18);
        child.color.push('yellow');
        console.log(child.color);  //输出打印 [1, 2, 3, "yellow"]
        console.log(child2.color); //输出打印 [1, 2, 3]
        console.log(child);
        console.log(child2);
        child.getName();
    
    image.png

    缺点:如上图发现Rectangle的原型的构造器constructor成了Shape,按照我们的理解应该是Rectangle,这就造成了构造器紊乱,

    组合继承(优化2)

    指定子类原型构造器constructor为自身

      // Shape - 父类(superclass)
        function Shape(age) {
            this.name = "123";
            this.color = [1,2,3];
            this.age = age;
        }
        // 父类的方法
        Shape.prototype.getName= function() {
            console.log(this.name);
        };
        // Rectangle - 子类(subclass)
        function Rectangle(age) {
            //向父类传递age参数
            Shape.call(this,age)  //或使用  Shape.apply(this) 差别在于传参方式不同
        }
        Rectangle.prototype = Shape.prototype;  //这里让子类原型引用父类原型
        Rectangle.prototype.constructor = Rectangle;   //让子类原型构造器指向自身
        var child = new Rectangle(10);
        var child2 = new Rectangle(18);
        child.color.push('yellow');
        console.log(child.color);  //输出打印 [1, 2, 3, "yellow"]
        console.log(child2.color); //输出打印 [1, 2, 3]
        console.log(child);
        console.log(child2);
        child.getName();
    
        //这时我们打印子类构造器能正确打印结果
        console.log(Rectangle.prototype.constructor)  //Rectangle
        //但是我们打印父类构造器出现了紊乱 也指向了子类Rectangle 因为父类原型是引用类型 当使用赋值操作时子类原型引用的是父类原型所指向的内存地址,当我们改变子类的原型constructor时同时也改变的父类的原型 constructor
        console.log(Shape.prototype.constructor) // Rectangle
    

    缺点:因为引用类型的特性,导致在修改子类的原型构造器同时也改变的父类的原型构造器

    组合继承(优化3

    设置一个中间对象,将中间对象的原型指向父类的原型

      // Shape - 父类(superclass)
        function Shape(age) {
            this.name = "123";
            this.color = [1,2,3];
            this.age = age;
        }
        // 父类的方法
        Shape.prototype.getName= function() {
            console.log(this.name);
        };
        // Rectangle - 子类(subclass)
        function Rectangle(age) {
            //向父类传递age参数
            Shape.call(this,age)  //或使用  Shape.apply(this) 差别在于传参方式不同
        }
        //设置中间对象(方式一)
        function f(){}
        f.prototype = Shape.prototype;
        Rectangle.prototype = new f();
    
        //上面三行代码也可以简化成(方式二)
        //Rectangle.prototype = Object.create(Shape.prototype);
    
        Rectangle.prototype.constructor = Rectangle
    
        var child = new Rectangle(10);
        var child2 = new Rectangle(18);
        child.color.push('yellow');
        console.log(child.color);  //输出打印 [1, 2, 3, "yellow"]
        console.log(child2.color); //输出打印 [1, 2, 3]
        console.log(child);
        console.log(child2);
        child.getName();
    

    ES6实现继承

    class Shape {
            constructor (age) {
                this.name = "123";
                this.color = [1,2,3];
                this.age = age;
            }
    
            getName () {
               console.log(this.name)
            }
        }
    
        class Rectangle extends Shape {
            constructor (age) {
                super(age)
            }
        }
    
        const o1 = new Rectangle(10);
        const o2 = new Rectangle(18);
        o1.color.push(4)
        console.log(o1.color)
        console.log(o2.color)
        o1.getName()
        console.log(o1)
        console.log(new Rectangle instanceof Shape)
    

    相关资料

    new 运算符
    constructor
    Object.create()
    Object.assign()
    深入理解JavaScript原型链与继承

    相关文章

      网友评论

          本文标题:JavaScript继承(原型链继承、构造函数继承、组合继承)

          本文链接:https://www.haomeiwen.com/subject/thqghltx.html