手写JS面试题

102 阅读2分钟

构造函数的继承方式及其优缺点

一,原型链继承

实现方式:将子类的prototype挂在父类的实例上

function Person() {
  this.name = '小明';
  this.eats = ['吃苹果'];
  this.getName = function () {
    console.log(this.name);
  };
}
Person.prototype.get = () => {
  console.log('Person.prototype上的方法');
};

function Child() {}
Child.prototype = new Person(); // 原型链继承

const child1 = new Child();
const child2 = new Child();

child1.name = '大儿子';
child1.eats.push('香蕉');
console.log(child1);
child1.get();

console.log('---------------');

child2.name = '二儿子';
console.log(child2);
child2.get();

image.png

  • 优点
    • 父类方法可以复用
  • 缺点
    • 父类所有的引用类型数据(对象,数组)会被子类共享,更改一个子类的数据,其他子类也会受到影响
    • 子类实例不能给父类构造函数传参

二,构造函数继承

实现方式: 在子类构造函数中用call函数修改父类构造函数的this

function Person() {
    this.name = '小明'
    this.eats = ['吃苹果']
    this.getName = function () {
    console.log(this.name)
    }
}

//   Person.prototype.get = () => {
//     console.log('Person.prototype上的方法') // 不注释会报错
//   }

function Child() {
    Person.call(this)
}

const child1 = new Child()
const child2 = new Child()

child1.name = '大儿子'
child1.eats.push('香蕉')
console.log(child1)
// child1.get()

console.log('---------------')

child2.name = '二儿子'
console.log(child2)
// child2.get()

image.png

  • 优点
    • 父类的引用类型不会被子类共享,不会互相影响
  • 缺点
    • 子类不能访问父类原型属性上的方法和参数

三,组合继承

实现方法原型链继承 + 构造函数继承

function Person() {
    this.name = '小明'
    this.eats = ['吃苹果']
    this.getName = function () {
    console.log(this.name)
    }
}

Person.prototype.get = () => {
    console.log('Person.prototype上的方法') 
}

function Child() {
    Person.call(this)
}
Child.prototype = new Person() 

const child1 = new Child()
const child2 = new Child()

child1.name = '大儿子'
child1.eats.push('香蕉')
console.log(child1)
child1.get()

console.log('---------------')

child2.name = '二儿子'
console.log(child2)
child2.get()

image.png

  • 优点
    • 父类可以复用
    • 父类构造函数中的引用属性不会被共享
  • 缺点
    • 会调用两次父类的构造函数,会有两份一样的属性和方法,影响性能

四,寄生组合继承(最优解)

实现方法

  1. 先用call再子类的构造函数中修改父类的this
  2. 定义一个中转的构造函数Fn,将Fn的prototype与父类的prototype绑定到一起,再将子类的prototype指定为Fn的实例
function Person() {
  this.name = '小明';
  this.eats = ['吃苹果'];
  this.getName = function () {
    console.log(this.name);
  };
}
Person.prototype.get = () => {
  console.log('Person.prototype上的方法');
};前端构造函数的继承方式及其优缺点1111111111111111111111111111111111

function Child() {
  Person.call(this);
}

const Fn = function () {};
Fn.prototype = Person.prototype;
Child.prototype = new Fn();

const child1 = new Child();
const child2 = new Child();

child1.name = '大儿子';
child1.eats.push('香蕉');
console.log(child1);
child1.get();

console.log('---------------');

child2.name = '二儿子';
console.log(child2);
child2.get();

image.png