介绍JS中继承的写法,包括使用prototype如何继承?使用Class语法糖如何继承?比较这两者之间的区别。

目录

  1. 如何使用prototype实现继承?
  2. 使用Class语法糖如何继承?
  3. 比较这两者之间的区别。

在ES6之前,JavaScript是没有类和继承这个概念的。ECMAScript 2015 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。类语法不会为JavaScript引入新的面向对象的继承模型。所以我们在ES6之前实现类和类的继承是通过原型链来实现的。

如何使用prototype实现继承?

首先我们需要定义一个对象,对象里存放对象自身的属性,同时对象的私有属性proto指向它的原型prototype,就可以存放对象的共有属性。例如:

function Human(name){
  this.name = name
}
Human.prototype.say = function(){
  console.log("我叫" + this.name + ",我在说话”)    
}

这样我们就实现了一个对象的定义和添加了对象的方法。如果我们要让对象的子对象拥有对象的方法,怎么实现呢?答案很简单,我们只要让子对象的私有属性proto指向父对象即可。

function Man(name){
  Human.call(this, name)
  this.sex = '男'    
}
Man.prototype._proto_ = Human.prototype

但是IE不支持man.prototype.proto = Human.prototype这种写法。所以我们只能通过新建一个空函数并让子对象指向空函数的方式来实现。

var f = {}
f.prototype = Human.prototype
man.prototype = new f()

实现原理是因为new运算符会

// 1. 首先创建一个空函数
var object = new Fn();
// 2. 将空对象的原型赋值为构造器函数的原型
object.__proto__ = Fn.prototype;
// 3. 更改构造器函数内部this,将其指向新创建的空对象
Fn.call(this);
// 4. 返回结果
return this

所以我们创建空函数的过程中就会让空函数指向它的原型对象。我们只要让Fn.prototype = new f()就可以。但是因为Fn.call(this)的存在,会让子对象也有父对象的方法,这是我们不想要的结果。所以,我们需要一个中间过渡的空函数。

让子对象的原型指向空函数,空函数的原型指向父对象。这样就能子对象中避免call(this)返回我们不想要的父对象的方法。

function Human(name){
    this.name = name
}
Human.prototype.say = function(){
    console.log("我叫"+this.name+",我在说话")
    return undefined
}
function Man(name){
    Human.call(this, name)
    this.sex = '男'
}

var f = function(){}
f.prototype = Human.prototype
Man.prototype = new f()

Man.prototype.sleep = function(){
    console.log('我在睡觉')
}

使用Class语法糖如何继承?

首先Class是ES6出现的语法糖。用Class实现继承的语法非常简单。代码如下:

class Human{
  constructor(name){
    this.name = name
  }
  say(){
    console.log("我叫" + this.name + ",我在说话")
  }
}
class Man extends Human{
  constructor(name){
    super(name)
    this.sex = '男'     
  }
  sleep(){
    console.log('我在睡觉')
  }
}

expends表示Man对象接上Human对象的原型链constructor调用了父类的构造函数。super表示传递的属性。

比较这两者之间的区别。

如果我们想在原型上声明一个属性,后者不支持声明属性。只能采取较为变通的写法:

Class声明变量

class Man extends Human{
  constructor(name){
    super(name)
    this.sex = '男'     
  }
  get 种族(){
    return '人类'
  }
  sleep(){
    console.log('我在睡觉')
  }
}

ES5声明变量

function Human(name){
    this.name = name
}
Human.prototype.种族 = '人类'
Human.prototype.say = function(){
    console.log("我叫"+this.name+",我在说话")
    return undefined
}

结论:前者比后者清晰,后者比前者简单。