六种继承方式(js)

1.简单原型链

        // 创建要继承的父类
        function Father() {
    
    
            // 自带的属性和方法
            this.name = "li";
            this.show = function() {
    
    
                console.log("帅");
            }
        };

        // 在父类的原型上添加方法
        Father.prototype.callback = function() {
    
    
            console.log(this.name);
        }

        // 定义Son
        function Son() {
    
    

        }

        // 令Son继承Father
        Son.prototype = new Father();// 这句是原型链继承的重点

        // 创建Son的对象实例
        var son = new Son();
        var son1 = new Son();

        console.log(son.name); // 可以调用继承于父类上的属性
        son.show(); //  可以调用继承于父类上的方法
        son1.callback(); // 可以调用father中原型上添加的方法

重点

让新实例的原型等于父类的实例。

优点

简单,易于实现

缺点

  1. 原型对象的引用属性是所有实例共享的。
        // 创建要继承的父类
        function Father() {
    
    
            // 自带的属性和方法
            this.name = "li";
        };

        // 定义Son
        function Son() {
    
    

        }

        // 令Son继承Father
        Son.prototype = new Father(); // 这句是原型链继承的重点

        // 创建Son的对象实例
        var son = new Son();
        var son1 = new Son();
        son1.__proto__.name = "zhang"; //通过一个实例改变Son的prototype上的name

        // 导致所有实例的name都改变,就叫做引用值共享
        console.log(son.name); //"zhang"
        console.log(son1.name); //"zhang"
  1. 创建子类实例时,无法向父类构造函数传参
  1. 继承单一

2. 构造函数式继承

        // 创建父类
        function Father(name) {
    
    
            this.name = name;
            this.arr = [1];
            this.show = function() {
    
    
                console.log("shuai");
            }
        }
        Father.prototype.call = function() {
    
    
            console.log("call");
        };
        // 创建子类
        function Son(name) {
    
    
            // 借父类的构造函数来增强子类实例,等于是把父类的实例属性复制了一份给子类实例装上了(完全没有用到原型)
            Father.call(this, name); // 核心代码
        }
        var son1 = new Son("n1");
        var son2 = new Son("n2");

        son1.arr.push(2); // 向son1的数组中添加一个数

        console.log(son1);
        console.log(son2);
        // 这时son实例的prototype的原型是object
        console.log(son1.show === son2.show); // false

重点

用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复制))

优点

  1. 只继承了父类构造函数的属性,没有继承父类原型的属性
  1. 解决了原型链继承的缺点
  1. 可以继承多个构造函数的属性(利用call())
  1. 在子实例中可向父实例传参。

缺点

  1. 只能继承父类构造函数的属性
  1. 无法实现构造函数的复用(每次使用时都要重新调用)
  1. 每个新实例都有父类构造函数的副本,臃肿

3. 组合继承(组合原型链继承和借用构造函数继承)(常用)

        // 创建父类
        function Father(name) {
    
    
            this.name = name;
            this.arr = [1];
            this.show = function() {
    
    
                console.log("shuai");
            }
        }
        Father.prototype.call = function() {
    
    
            console.log("call");
        };

        function Son(name) {
    
    
            Father.call(this, name); // 借用构造函数继承
        };

        Son.prototype = new Father(); // 原型链继承

        var son1 = new Son('li');
        var son2 = new Son('zhang');
        console.log(son1);
        console.log(son2);

重点

结合了两种模式的优点,传参和复用

优点

  1. 可以继承父类上的属性,可以传参,可复用
  1. 每个新实例引入的构造函数属性是私有的

缺点

调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。

4. 原型式继承

        // 创建父类
        function Father(name) {
    
    
            this.name = name;
            this.arr = [1];
            this.show = function() {
    
    
                console.log("shuai");
            }
        }

        Father.prototype.call = function() {
    
    
            console.log("call");
        };
        // 生育函数
        function beget(obj) {
    
    
            function F() {
    
    }
            F.prototype = obj; //继承了传入的参数
            return new F(); //返回函数对象
        }

        var son1 = new Father(); // 先拿到父类的对象
        console.log(son1);

        // (核心代码)
        var son2 = beget(son1); // 将父类中的属性赋值到每一个子类的函数的原型中
        var son3 = beget(son1); // 同上  

        son2.name = "zhang"; // 修改实例中的属性

        console.log(son2);
        console.log(son3);
        console.log(son2.call); //子类的实例都可以访问父类的原型上的属性和方法

重点

用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。(每创建一个对象实例都会返回一个新的函数)、

优点

复制一个对象,用函数来包装。

缺点

  1. 所有实例都会继承原型上的属性。
  1. 无法实现复用。

五.寄生式继承

        // 创建父类
        function Father(name) {
    
    
            this.name = name;
            this.arr = [1];
            this.show = function() {
    
    
                console.log("shuai");
            }
        }

        Father.prototype.call = function() {
    
    
            console.log("call");
        };

        function beget(obj) {
    
    
            function F() {
    
    }
            F.prototype = obj; //继承了传入的参数
            return new F(); //返回函数对象
        }

        var son = new Father();



        // 将原型式继承的核心代码套一个壳子
        function getSubObject(obj) {
    
    
            var clone = beget(obj); // 核心
            return clone;
        }

        var son1 = getSubObject(new Father());
        console.log(son1);

重点

就是给原型式继承外面套了个壳子。
优点

没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。

缺点

没用到原型,无法复用。

六、寄生组合式继承(常用)

寄生

在函数内返回对象然后调用

组合

1、函数的原型等于另一个实例。

2、在函数中用apply或者call引入另一个构造函数,可传参

        // 创建父类
        function Father(name) {
    
    
            this.name = name;
            this.arr = [1];
            this.show = function() {
    
    
                console.log("shuai");
            }
        }

        Father.prototype.call = function() {
    
    
            console.log("call");
        };

        function beget(obj) {
    
    
            function F() {
    
    }
            F.prototype = obj; //继承了传入的参数
            return new F(); //返回函数对象
        }

        var bet = beget(Father.prototype);
        // 继承了父类函数的原型
        
        function Son() {
    
    
            Father.call(this);
        }
        Son.prototype = bet; //继承了get的实例;

        bet.constructor = Son; // 修复实例
        var son1 = new Son();
        // Sub的实例就继承了构造函数的属性,父类实例,
        console.log(son1.call);

重点:修复了组合继承的问题

猜你喜欢

转载自blog.csdn.net/weixin_45773503/article/details/108758825