ES5和ES6类的创建与继承--含原型和原型链

通过类的创建与继承解读原型和原型链

在开始之前,先和大家说明

es5中的类用的是构造函数,es6才有了类(本质还是构造函数)

类创建的实例有两种属性,一种是实例自身的,一种是公共的(公共的存在原型对象中)

ES5

es5中构造函数就是类,所以下文可能会出现两种称呼,我们知道是一个东西就好

构造函数的创建与实例化对象
// 类中创建的实例有两种属性,一种是实例自身的
function Animal(){
  this.type = '哺乳类' 
}
// 一种是通过原型对象绑定的 公共的
// 这些公共的方法,可以让所有的实例对象共享
// 即构造函数的prototype
Animal.prototype.eat = function(){
  console.log('eat');
}

let animal = new Animal
// 实例的animal对象具有类中所有的属性和方法
// 实例的__proto__指向构造函数的原型对象
console.log(animal.__proto__ === Animal.prototype);
// 输出true
constructor

原型对象中有一个constructor,里面存放着构造函数本身

// 上面的内容中,我们讲到了
// Animal.prototype是自己的原型对象
// animal.__proto__指向Animal.prototype
console.log(Animal.prototype.constructor);
// 输出的是构造函数Animal的代码

请添加图片描述

// 有时候我们代码写的非常狠啊
// 不是给原型对象追加内容,而是直接重写修改了原型对象
Animal.prototype = {
    fun:function(){
    	console.log('我重写了原型对象');        
    }
}
// 此时constructor已经被我们重写搞没了
// 所以需要将constructor指回去
Animal.prototype = {
    fun:function(){
    	console.log('我重写了原型对象');        
    }
    constructor:Animal
}

通过上面的介绍,我们已经基本了解了构造函数实例对象的一套流程,以及他们的一些属性,接下来我们将讲一讲es5中的继承

构造函数的继承与原型链

在实际的使用场景中,Animal属于一种很宽泛的类,我们一般不会将其实例化,而是让更具体的类去继承他的属性

function Animal(){
  // new有一个target属性
  // 限制不能new Animal
  if(new.target === Animal){
    // 如果new了一个Animal类,就抛出错误
    throw new Error('animal类不能被new 可以被继承')
  }
  this.type = '哺乳类'
}
Animal.prototype.eat = function(){
  console.log('eat');
}

// 创建Tiger类并继承Animal类
function Tiger(){
  // 继承父类的属性
  Animal.call(this)
}
// 继承父类原型对象
Tiger.prototype.__proto__ = Animal.prototype
// 这里提前穿插一句es6的写法
// 上面继承原型对象写法很不优雅,于是es6有了
Object.setPrototypeOf(Tiger.prototype,Animal.prototype)
继承原型对象

上面在继承父类原型对象时,可能会产生这样的疑问,为什么不直接把子类的原型对象指向父类,像下面这样

// 错误写法
Tiger.prototype = Animal.prototype

这样写会造成,二者所指向的是同一块原型对象,那修改子类的原型对象时,父类也会跟着变,就乱套了

所以我们采用的是原型链的形式

Tiger.prototype.__proto__ = Animal.prototype

请添加图片描述

上图即为原型链,tiger实例会先从自己的__proto__也就是Tiger的原型对象开始寻找方法,找不到再从Tiger.prototype.__proto__寻找,找不到再从Animal.prototype.__proto__寻找,找不到再到Object.prototype.__proto__,最后到null没找到,那就确实没有

这,就是原型链

继承原型对象还有一种方法(不建议)

Tiger.prototype = Object.create(Animal.prototype)

这种方法相当于继承了创建了一个第三方的Animal.prototype,然后将Tiger.prototype指向这个第三方的Animal原型对象,也能完成操作

ES6

es6就有了类的概念,相对来说会便捷很多

类的创建
// es6里提供了类,类只能new
class Animal{
  // es6为我们提供了constructor()写类自身的属性
  constructor(){
    if(new.target === Animal){
      throw new Error('animal类不能被new 可以被继承')
    }
    this.type = '哺乳类'
  }
  // constructor()外面写的就是原型上的
  eat(){
    console.log('eat');
  }

  // 清清脑子,下面这样独特的写法虽然在外面,但是属于Animal类自己的静态属性
  static get flag(){
    return '动物'
  }
}

// 类自己的静态属性当然可以直接调用
console.log(Animal.flag);
// 输出:动物
类的继承
// es6的继承
// extends自动进行了下面的操作
// Tiger.__proto__ = Animal
// Animal.call(this)
// Tiger.prototype = Object.create(Animal.prototype)
class Tiger extends Animal{

}
let tiger = new Tiger()
console.log(Tiger.flag);
console.log(tiger.type);
console.log(tiger.eat());
// 上面的内容都能正常输出

如果只是单纯的继承的话,上面就完成了

但是当我们要写一些自己的东西时,就要重新搞了

// 什么都不写,extends会帮你做好
// 但是 你如果你要自己重写constructor() 那就要重新搞了
class Tiger extends Animal{
  constructor(){
    // 在使用this之前必须调用super
    super(); // 功能和Animal.call(this)
  }
  // 接下来,我们如果想在子类的属性或方法中调用父类的,就使用super
  static getFlag(){
    return super.flag
  }
  eat(){
    // 重写类
    // 调用父类的原型
    super.eat()
    console.log('我调用到了父类的方法');
  }
}
let tiger = new Tiger()
console.log(Tiger.flag);
console.log(Tiger.getFlag());
console.log(tiger.eat());
// 上面的内容都能正常输出
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的战斗鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值