JS 中的继承

作者: 弱冠而不立 | 来源:发表于2021-04-01 00:57 被阅读0次

    推荐文章:一篇文章理解JS继承——原型链/构造函数/组合/原型式/寄生式/寄生组合/Class extends

    继承方式的分类

    原型链继承

    • 重点:子的原型对象为new一个父的实例 Child.prototype = new Parent();
    • 缺点:多个实例对引用类型属性的操作会被篡改;在创建Child实例时,不能向 Parent 传参
    function Animal() {}
    function Dog(){}  
    Dog.prototype = new Animal();
    

    引用类型的属性被所有实例共享的栗子

    function Parent () {
        this.names = ['kevin', 'daisy'];
    }
    function Child () {
    }
    Child.prototype = new Parent();
    
    var child1 = new Child();
    child1.names.push('yayu');
    console.log(child1.names); // ["kevin", "daisy", "yayu"]
    
    var child2 = new Child();
    console.log(child2.names); // ["kevin", "daisy", "yayu"]
    

    构造函数继承

    • 重点:在子构造函数内部调用父构造函数 Parent.call(this)
    • 缺点:方法都在构造函数中定义,每次创建实例都会创建一遍方法
    function Cat(name) {            
        Animal.call(this, name);   // 核心,把父类的实例方法属性指向子类
    }
    

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

    • 重点:使用原型链继承共享的属性和方法,通过借用构造函数继承实例属性
    • 优点:
      1、创建子类实例,可以向父类构造函数传参数;
      2、父类的实例方法定义在父类的原型对象上,可以实现方法复用;
      3、不共享父类的构造方法及属性;
    • 缺点:无论在什么情况都会调用两次父构造函数,一次是创建子类型原型,另一次是在子构造函数内部
    function Cat(name) {
        // 核心,把父类的实例方法属性指向子类。即,继承属性 。                 
        Animal.call(this, name);                          
    }
    // 核心, 父类的实例作为子类的原型对象,即继承方法
    Cat.prototype = new Animal()
     // 修复子类Cat的构造器指向,防止原型链的混乱                                
    Cat.prototype.constructor = Cat;                           
    

    原型式继承

    • 重点:将源对象赋给目标对象的原型,原型式继承的object方法本质上是对参数对象的一个浅复制。
    • 优点:父类方法可以复用
    • 缺点:和原型链继承一样,父类的引用属性会被所有子类实例共享、子类构建实例时不能向父类传递参数。

    实现方式 1:

        function object(o) {
            // 创建临时新的构造函数
            function F() { }
            // 将传入的这个对象作为这个构造函数的原型
            F.prototype = o;
            // 返回这个临时类型的一个新的实例
            return new F();
        }
    
        let person = {
            name: "可莉",
            friends: ["七七", "雷泽"]
        }
    
        let anotherPerson = object(person);
        anotherPerson.name = "七七";
        anotherPerson.friends.push("可莉");
    
        console.log(person);
        //{name: "可莉", friends: ["七七", "雷泽", "可莉"]}
        console.log(anotherPerson);
        //{name: "七七", __proto__: person}
    

    实现方法 2:
    Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。规范了原型式继承。
    方法接收两个参数,一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象,这第二个参数和 Object.defineProperties() 方法的第二个参数格式相同。

        let anotherPerson = Object.create(person, {
            name: {
                value: "琴团长"
            }
        });
        console.log(anotherPerson);
        //{name: "琴团长", __proto__: person}
    

    寄生式继承

    • 重点:就在原型式继承的基础上,给浅复制对象增加方法,增强了这个浅复制对象的能力。
        function createOther(original) {
            let clone = Object.create(original);
            // 以某种方式增强这个对象
            clone.say = function() {
                console.log("sayHi");
            } 
            return clone;
        }
    

    寄生组合继承

    function Cat(name) {
         // 核心,把父类的实例方法属性指向子类;                     
        Animal.call(this, this.name);                         
    }
    // 核心,利用空对象作为中介;
    var F = function(){};
     // 核心,将父类的原型赋值给空对象F;                                       
    F.prototype = Animal.prototype; 
     // 核心,将F的实例赋值给子类;                           
    Cat.prototype = new F(); 
    // 修复子类Cat的构造器指向,防止原型链的混乱;                                 
    Cat.prototype.constructor = Cat;                            
    
        function Father(name) {
            this.name = name;
        }
    
        Father.prototype.sayName = function() {
            console.log(this.name);
        }
    
        function inheritPrototype(f_constructor, c_constructor) {
            // 创建父类原型的浅复制,(原型式继承) 
            let prototype = Object.create(f_constructor.prototype);
            // 修正原型上构造函数的指向
            prototype.constructor = child;
            // 将子类的原型替换为这个原型
            c_constructor.prototype = prototype;
            
            
        }
    
        // 继承父类上的属性, (构造函数继承)
        function Child(name, age) {
            Father.call(this, name);
            this.age = age;
        }
    
        inheritPrototype(Father, Child);
    

    ES6 class extends关键字继承

    • 重点:子类必须在 constructor 方法中调用 super 方法,其次就算你子类不写 constructor 方法,但是编译的时候还是会默认给你加上。
    • 和 ES5 其他各种思路的区别:
      其实上面各种形式,但凡只要使用到了构造函数的继承,都是子类的实例 (this) 调用父类的构造函数。但是 ES6 的 extends 的核心概念就是父类实例对象的属性和方法,加到this上面,然后再用子类的构造函数修改 this
    class Animal {
        constructor(name) {
            this.name = name;
        }
    
        sayName() {
            console.log(this.name)
        }
    }
    
    class Dog extends Animal {
        constructor(name, age) {
            // 这个 super 就相当于调用父类的构造函数,调用完成之后就得到了与父类属性和方法
            // 否则如果不调用super子类就得不到this对象
            // 简而言之,调用super就能通过父类的构造函数创建子类的 this 对象
            super(name);
            this.age = age;
        }
    
        wangwang() {
            console.log("wangwang~~~~");
        }
    }
    
    const dog1 = new Dog("teddy", 18);
    
    dog1.sayName();
    dog1.wangwang();
    

    相关文章

      网友评论

        本文标题:JS 中的继承

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