技术实践

原型继承和 Class 继承

class 实现继承的核心在于使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super,因为这段代码可以看成 Pa

羊先生的头像
羊先生2020.02.17 · 5 分钟阅读 · 75 阅读
原型继承和 Class 继承封面
文章正文还需 5 分钟

首先先来讲下 class,其实在 JS 中并不存在类,class 只是语法糖,本质还是函数

js 复制代码
class Person {}
Person instanceof Function // true

构造函数继承

js 复制代码
function Parent1 (name) {
  this.name = name;
  this.getName = function(){
       return 1;
  }
}

Parent1.prototype.age = 10;
Parent1.prototype.getAge = function(){
   return this.age;
};

function Child1 (name) {
    // call 和 apply 改变函数上下文 将this指向父类Parent1 缺点:无法继承prototype上的属性和方法
    Parent1.call(this,name);
    this.type = 'child1';
}
var s = new Child1('羊先生');
console.log(s.name)

缺点: 无法继承prototype上的属性和方法

js 复制代码
console.log(s.age) // undefined

优点: 可以传递参数

apply: 最多只能有两个参数——新this对象和一个数组argArray。如果给该方法传递多个参数,则把参数都写进这个数组里面,当然,即使只有一个参数,也要写进数组里。如果argArray不是一个有效的数组或arguments对象,那么将导致一个TypeError。如果没有提供argArray和this任何一个参数,那么Global对象将被用作this,并且无法被传递任何参数

call: 它可以接受多个参数,第一个参数与apply一样,后面则是一串参数列表。这个方法主要用在js对象各方法相互调用的时候,使当前this实例指针保持一致,或者在特殊情况下需要改变this指针。如果没有提供this参数,那么 Global 对象被用作this

利用原型链实现继承

js 复制代码
function Parent2 () {
    this.name = 'parent2';
    this.play = [1, 2, 3];
}
function Child2 () {
    this.type = 'child2';
}

Parent2.prototype.age = 10;
Parent2.prototype.getAge = function(){
    return this.age;
};

Child2.prototype = new Parent2(); //将Child2的prototype属性赋值为父类
    
var s1 = new Child2();
var s2 = new Child2();

s1.play.push(4);  //往S1实例中的paly属性增加一个4,
console.log(s1.play, s2.play);  //打印结果为 1,2,3,4    1,2,3,4

这种继承方式解决了上面的prototype不能继承的问题,但是又有一个新的问题,当生成多个实例的时候,修改其中一个实例的属性,其他实例的属性也会发生改变

组合继承—方式1

js 复制代码
function Parent3 () {
   this.name = 'parent3';
   this.play = [1, 2, 3];
}
function Child3 () {
   Parent3.call(this);
   this.type = 'child3';
}

Parent3.prototype.age = 10;
Parent3.prototype.getAge = function(){
   return this.age;
};

Child3.prototype = new Parent3();

var s3 = new Child3();
var s4 = new Child3();

s3.play.push(4);
console.log(s3.play, s4.play);//1,2,3,4 //1,2,3

缺点:解决上面属性的共享问题,但是Parent3 执行了2次new,第一次是构造,第二次是实例化

组合继承—方式2

js 复制代码
function Parent(name) {
    this.name = name;
    this.arr = [1];
}
Parent.prototype.say = function() { 
    console.log('hello')
}
function Child(name,like) {
    Parent.call(this,name,like) 
    this.like = like;
}
Child.prototype = Object.create(Parent.prototype)

Child.prototype.constructor = Child //这里是修正构造函数指向的代码

let boy1 = new Child('小红','apple')
let boy2 = new Child('小明','orange')
let p1 = new Parent('小爸爸')

console.log(boy1.constructor); // Child
console.log(p1.constructor);// Parent 

Object.create: 方法创建一个新对象,使用现有的对象来提供新创建的对象的proto,更多详解详解Object.create(null)

Class 继承

以上几种继承方式都是通过原型去解决的,在 ES6 中,我们可以使用 class 去实现继承,并且实现起来很简单

js 复制代码
class Parent {
  constructor(value) {
    this.val = value
  }
  getValue() {
    console.log(this.val)
  }
}

class Child extends Parent {
  constructor(value) {
    super(value)
  }
}

let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true

class 实现继承的核心在于使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super,因为这段代码可以看成 Parent.call(this, value),当然了,之前也说了在 JS 中并不存在类,class 的本质就是函数

更多优秀文章

JavaScript深入之继承的多种方式和优缺点
如何继承Date对象?由一道题彻底弄懂JS继承
原型链/构造函数/组合/原型式/寄生式/寄生组合/Class extends
Javascript ES6 Class继承的原理