JS的几种继承方式及使用方式和适用场景总结

1. 原型链继承

  • 特点:简单易懂,但是所有实例共享原型上的属性,如果原型上定义的是引用类型值,那么所有实例会共享同一份数据。
  • 适用场景:当不需要在父类的原型上定义引用类型属性时,或者当需要共享某些状态时可以使用原型链继承。
  •           {//原型链继承
                 function Person(name){
                    this.name = name;
    
                 }
                 Person.prototype.getName = function(){
                    return this.name;
                 };
                 function Student(name,grade){
                    Person.call(this,name);//调用父类构造函数
                    this.grade = grade;
    
                 }
                 //让'Student'继承'Person'
                 Student.prototype = Object.create(Person.prototype);
                 Student.prototype.constructor = Student;
                 
                 const student = new Student("Alice",10);
                 console.log(student.getName());//Alice
                 console.log(student.grade);//10
                 console.log(student.name);//Alice
                 
                }
    

2. 构造函数继承

  • 特点:每个实例都有自己的一份属性副本,不会共享引用类型的数据,但是无法实现函数复用,每次创建子类实例时都需要重新执行父类构造函数。
  • 适用场景:当需要每个实例都拥有独立的属性副本时,或者当需要在子类构造函数中执行一些特定的初始化操作时。
  •             {//构造函数继承
                 //构造函数继承的主要特点是每次示例都有自己的一份属性副本,不会共享引用类型的数据。但是,这种方法无法实现函数复用,每次
                 //创建子类实例时都需要重新执行父类构造函数。
                 function Person(name){
                    this.name = name;
                 }
                 Person.prototype.getName = function(){
                    return this.name;
                 }
                 function Student(name,grade){
                    Person.call(this,name);
                    this.grade = grade;
                 }
                 const student = new Student('Alice',10);
                 console.log(student.getName());//Alice
                 console.log(student.grade);//10
                 console.log(student.name);//Alice
    
                }

3. 组合继承

  • 特点:结合了原型链继承和构造函数继承的优点,既实现了函数复用(不必在每个实例中重新定义函数),又保证了每个实例都有自己的属性副本。
  • 适用场景:当需要实现函数复用并且每个实例都需要拥有独立的属性副本时,组合继承是最常用的选择之一。

4. 原型式继承(使用 Object.create

  • 特点:允许创建一个新对象,使其继承另一个对象的状态,而不需要构造函数或原型链。
  • 适用场景:当需要继承一个纯粹的对象(不是构造函数实例)的状态时,例如创建一个具有特定属性配置的新对象。
  • // 定义一个对象
    const person = {
        name: 'Parent',
        age: 30,
        getName: function() {
            return this.name;
        },
        getAge: function() {
            return this.age;
        }
    };
    
    // 使用 Object.create 创建新对象
    const student1 = Object.create(person);
    const student2 = Object.create(person);
    
    // 为 student1 添加特定的属性
    student1.name = 'Alice';
    student1.age = 15;
    student1.grade = 10;
    
    // 为 student2 添加特定的属性
    student2.name = 'Bob';
    student2.age = 16;
    student2.grade = 11;
    
    // 测试
    console.log(student1.getName()); // 输出: Alice
    console.log(student1.getAge());  // 输出: 15
    console.log(student1.grade);     // 输出: 10
    
    console.log(student2.getName()); // 输出: Bob
    console.log(student2.getAge());  // 输出: 16
    console.log(student2.grade);     // 输出: 11

5. 寄生式继承

  • 特点:基于原型式继承,但在创建新对象之后,可以增强这个对象,为其添加更多的功能。
  • 适用场景:当需要对继承的对象进行定制化修改或增强时。
  • const person = {
        name: 'Parent',
        age: 30,
        getName: function() {
            return this.name;
        },
        getAge: function() {
            return this.age;
        }
    };
    
    function createStudent(original, grade, school) {
        const student = Object.create(original);
        student.grade = grade;
        student.school = school;
        student.getGrade = function() {
            return this.grade;
        };
        return student;
    }
    
    const student1 = createStudent(person, 10, 'XYZ School');
    const student2 = createStudent(person, 11, 'ABC School');
    
    console.log(student1.getName()); // 输出: Parent
    console.log(student1.getAge());  // 输出: 30
    console.log(student1.grade);     // 输出: 10
    console.log(student1.school);    // 输出: XYZ School
    console.log(student1.getGrade()); // 输出: 10
    
    console.log(student2.getName()); // 输出: Parent
    console.log(student2.getAge());  // 输出: 30
    console.log(student2.grade);     // 输出: 11
    console.log(student2.school);    // 输出: ABC School
    console.log(student2.getGrade()); // 输出: 11

6. 寄生组合式继承

  • 特点:避免了组合继承中两次调用父类构造函数的问题,通过复制父类原型的属性到子类原型上。
  • 适用场景:当希望避免多次调用父类构造函数带来的性能问题时。
  • function Parent(name) {
        this.name = name;
        this.colors = ['red', 'blue', 'green'];
    }
    
    Parent.prototype.getName = function() {
        return this.name;
    };
    function Child(name, toy) {
        Parent.call(this, name); // 调用父类构造函数
        this.toy = toy;          // 子类特有的属性
    }
    
    // 寄生组合式继承的关键部分
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
    
    // 创建子类实例
    const child1 = new Child('Alice', 'doll');
    const child2 = new Child('Bob', 'car');
    
    // 测试
    console.log(child1.getName()); // 输出: Alice
    console.log(child1.toy);       // 输出: doll
    console.log(child1.colors);    // 输出: ['red', 'blue', 'green']
    
    console.log(child2.getName()); // 输出: Bob
    console.log(child2.toy);       // 输出: car
    console.log(child2.colors);    // 输出: ['red', 'blue', 'green']

7. ES6 类继承

  • 特点:提供了更接近传统面向对象语言的语法糖,使得继承更加直观,易于理解和维护。
  • 适用场景:在现代的JavaScript开发中,特别是在使用模块化和构建工具如Webpack的环境中,ES6类继承因其简洁性和标准支持而被广泛采用。
  •         class Parent{
                constructor(){
                    this.name = 'parent';
                }
                getName(){
                    return this.name;
                }
    
               }
               class Child extends Parent{
                constructor() {
                    super();
                    this.type="child";
                }
               }
               const child = new Child();
               console.log(child.getName());//parent
               console.log(child.type);//child
               console.log(child.name);//parent

总的来说,选择哪种继承方式取决于项目的具体需求、代码的可维护性、性能考虑以及是否遵循现代的JavaScript最佳实践。对于新的项目,建议优先考虑使用ES6的类继承,因为它不仅语法简洁,而且得到了广泛的浏览器支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值