MST

星途 面试题库

面试题:JavaScript中如何利用对象扩展能力实现继承

请详细阐述在JavaScript中,利用对象扩展能力实现继承的至少两种方式,并说明它们的优缺点。例如,原型链继承、构造函数继承、组合继承等方式的具体实现及各自特性。
34.0万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

原型链继承

  1. 实现方式
    function Parent() {
        this.value = 1;
    }
    Parent.prototype.getValue = function() {
        return this.value;
    };
    
    function Child() {}
    Child.prototype = new Parent();
    Child.prototype.constructor = Child;
    
    let child = new Child();
    console.log(child.getValue()); 
    
  2. 优点
    • 非常纯粹的继承关系,子类可以访问到父类原型上的所有属性和方法。
    • 实现简单,易于理解。
  3. 缺点
    • 所有子类实例共享父类实例的属性,若父类属性为引用类型,一个子类实例对其修改会影响其他子类实例。例如,若 Parent 中有一个数组属性,一个 Child 实例向该数组添加元素,其他 Child 实例访问该数组时也会看到新增元素。
    • 创建子类实例时,无法向父类构造函数传参。

构造函数继承

  1. 实现方式
    function Parent(name) {
        this.name = name;
        this.colors = ['red', 'blue'];
    }
    
    function Child(name, age) {
        Parent.call(this, name);
        this.age = age;
    }
    
    let child = new Child('John', 20);
    console.log(child.name); 
    console.log(child.age); 
    
  2. 优点
    • 解决了子类实例共享父类实例属性的问题,每个子类实例都有自己独立的属性。
    • 可以在创建子类实例时向父类构造函数传递参数。
  3. 缺点
    • 子类无法访问父类原型上的方法,只能访问父类构造函数内部定义的属性和方法,代码复用性差。

组合继承

  1. 实现方式
    function Parent(name) {
        this.name = name;
        this.colors = ['red', 'blue'];
    }
    Parent.prototype.sayName = function() {
        console.log(this.name);
    };
    
    function Child(name, age) {
        Parent.call(this, name);
        this.age = age;
    }
    Child.prototype = new Parent();
    Child.prototype.constructor = Child;
    
    let child = new Child('John', 20);
    child.colors.push('green');
    console.log(child.colors); 
    child.sayName(); 
    
  2. 优点
    • 融合了原型链继承和构造函数继承的优点,既实现了函数复用(可以访问父类原型方法),又保证了每个子类实例有自己独立的属性。
  3. 缺点
    • 父类构造函数会被调用两次,一次是在 Child.prototype = new Parent(),另一次是在 Parent.call(this, name) 中,造成了不必要的性能开销。

寄生组合继承

  1. 实现方式
    function inheritPrototype(Child, Parent) {
        let prototype = Object.create(Parent.prototype); 
        prototype.constructor = Child;
        Child.prototype = prototype;
    }
    
    function Parent(name) {
        this.name = name;
        this.colors = ['red', 'blue'];
    }
    Parent.prototype.sayName = function() {
        console.log(this.name);
    };
    
    function Child(name, age) {
        Parent.call(this, name);
        this.age = age;
    }
    
    inheritPrototype(Child, Parent);
    
    let child = new Child('John', 20);
    child.colors.push('green');
    console.log(child.colors); 
    child.sayName(); 
    
  2. 优点
    • 解决了组合继承中父类构造函数被调用两次的问题,只调用一次父类构造函数,提高了性能。
    • 同时保留了组合继承的优点,既实现了函数复用,又保证子类实例有独立属性。
  3. 缺点
    • 实现相对复杂,需要额外的辅助函数(如 inheritPrototype)来处理原型继承关系。

ES6 Class继承

  1. 实现方式
    class Parent {
        constructor(name) {
            this.name = name;
            this.colors = ['red', 'blue'];
        }
        sayName() {
            console.log(this.name);
        }
    }
    
    class Child extends Parent {
        constructor(name, age) {
            super(name);
            this.age = age;
        }
    }
    
    let child = new Child('John', 20);
    child.colors.push('green');
    console.log(child.colors); 
    child.sayName(); 
    
  2. 优点
    • 语法更加简洁、清晰,更符合面向对象编程的习惯。
    • 实现了原型链继承和构造函数继承的合理融合,避免了父类构造函数多次调用的问题。
    • 类的定义和继承结构一目了然,易于维护和理解。
  3. 缺点
    • 兼容性不如传统继承方式,在一些老旧浏览器中可能需要使用转译工具(如 Babel)进行转换才能正常运行。