总结:js中的继承

214 阅读2分钟

js中的继承就是让子类能够访问父类的属性和方法。

1. 原型链继承

原型链继承就是让子类的原型指向父类的实例,借助原型链的查找规则,让子类能够查找到父类身上。

Parent.prototype.say = function () {
  console.log('hello');
}

function Parent() {  // 形参age
  this.name = 'parent';
  this.age = 50   // this.age = age,这样子类就无法给父类传值了
}

// 原型链继承
Child.prototype = new Parent();    // {name: 'parent', age: 50}.__proto__ === Parent.prototype

function Child() {
  this.name = 'child';
}

const c = new Child();
console.log(c.name);
console.log(c.age);
c.say();

但是不能子类无法给父类传值。

2. 构造函数继承

在子类调用父类构造函数,这样就可以继承父类的属性和方法。但是无法继承父类的原型。

Parent.prototype.say = function () {
  console.log('hello');
}

function Parent(age) {
  this.name = 'parent';
  this.age = age
}

function Child(name, age) {
  // 父类构造函数
  Parent.call(this, age);  // 让 Parent 里面的 this 指到 Child 身上
  this.name = name;
}

const c = new Child('child', 50);
console.log(c.name);
console.log(c.age);
c.say();    // 报错,无法继承父类的原型

3. 组合继承

就是将原型链继承与构造函数继承结合起来。但是父类构造函数执行了两次

Parent.prototype.say = function () {
  console.log('hello');
}

function Parent(age) {
  this.name = 'parent';
  this.age = age
}

// 原型链继承
Child.prototype = new Parent();

function Child(name, age) {
  // 构造函数
  Parent.call(this, age);  // 让 Parent 里面的 this 指到 Child 身上
  this.name = name;
}

const c = new Child('child', 50);
console.log(c.name);
console.log(c.age);
c.say();    

4. 原型式继承

Object.create(obj)就是创建一个对象,让该对象的隐式原型指向obj,如newObj = Object.create(obj),newObj的隐式原型就是obj。这样就可以实现继承。

const obj = {
  a: 1,
  b: 2,
  c: 3
}

let newObj = Object.create(obj)  // newObj.__proto__ = obj
console.log(newObj);       //  {}
console.log(newObj.a);     // 1

5. 寄生组合式继承

使用Object.create(Parent.prototype)创建一个空对象,让这个对象的隐式原型等于Parent.prototype,然后将该对象赋值给Child的显示原型。也就是Child.prototype.__proto__ = Parent.prototype

Parent.prototype.say = function () {
  console.log('hello');
}

function Parent(age) {    // 相当于Parent =new Function
  this.name = 'parent';
  this.age = age
}

// 创建一个空对象,让这个对象的隐式原型等于Parent.prototype,然后将该对象赋值给Child的显示原型
// Child.prototype.__proto__ = Parent.prototype
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child;  // 让 Child 继承 Parent 上的 constructor

function Child(name, age) {
  Parent.call(this, age);  // 继承 Parent 的属性和方法
  this.name = name;
}

const c = new Child('child', 50);
console.log(c.name);
console.log(c.age);
c.say();

console.log(c.constructor);  // 表示 c 对象是由谁创建的,是Child.prototype身上的

其中c.constructor表示 c 对象是由谁创建的,是Child.prototype身上的,所以如果不加Child.prototype.constructor = Child;就会导致c对象是由Parent创建的。

也就是本来Child.prototype.constructor = Child,但是后面Child.prototype又重新赋值为一个空对象,后面再访问就访问不到了,但是由于Child.prototype.__proto__=Parent.prototype,也就是这个空对象有个隐式原型,会顺着隐式原型向上访问,访问到Parent.prototype身上去了。

6. class继承: extends + super()

在es6,添加了class继承(extends + super()),使得继承更加直观优雅。

class Parent {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
  //  相当于定义在class类的原型上
  say() {
    console.log('hello');
  }
}

class Child extends Parent {
  constructor(sex, name, age) {
    super(name, age)
    this.sex = sex
  }
}

const c = new Child('男', '张三', 18)
console.log(c);   // Child { name: '张三', age: 18, sex: '男' }
c.say()    // hello