js继承篇

183 阅读4分钟

前言

js原型这块,之前是半蒙半懂,今天终于在闲暇之余将这个概念深入到底,下面是我的个人理解,如果错误请指正。

继承的概念

继承是面向对象软件技术当中的一个概念,与多态、封装共为面向对象的三个基本特征。继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等

js如何定义继承

js中没有类的概念,只有对象(函数也是对象),每一个对象的都有一个私有属性__proto__指向它的构造函数的prototype,该原型对象又有__proto__属性指向它的构造函数的prototype,层层向上直到一个对象的原型对象为 null

let fn=function(){}
fn.__proto__.__proto__.__proto__==null			//true
fn instanceof Function					//true

注意!Function.prototype 是 函数 RegExp.prototype 是 正则表达式 Array.prototype 是 数组

如上图所示:

fn.__proto__指向Function.prototype

Function.prototype.__proto__指向Object.prototype

Object.prototype.__proto__是null

便于理解,构造函数称呼改为class, 只要x.__proto__指向y的prototype,那么x就拥有了y的一些属性和方法(可以理解为x就是y的实例),那么fn(这个Funtion类的实例)通过层层原型链,拥有了Object类的属性和方法,所以可以说Function类继承了Object类

这种基于原型链的方式就是js的继承方式

new一个对象

  1. 如果用new关键词生成实例,那么实例都可以使用prototype里的属性和方法; 原理是js的原型链,在当前obj上找不到此属性或方法的时候,就在obj.__proto__上找,一直找,直到找到为止,如果当...__proto__==null的时候都没有找到,那么就报undefined

  2. js函数没有放在prototype的属性和方法,只有函数本身可以使用,不可以被继承,比如Number.max() Date.now(),也可称为静态方法

  3. js对象有三种构建方式:

    1){}对象字面量:可以理解为若干个属性集合,值可以是基本类型或者object类型

    2)new Fn()通过构造函数创建

    3)Object.create(null) 可以创建一个没有原型链的对象

  4. js继承这个说法不是很准确,因为js的继承并不是将父类的属性和方法复制到子类,而是js引擎通过原型链寻找属性和方法,所以用委托这个词比较贴切

借用其他同学的例图:

function SuperType() {
    this.property = true;
}

SuperType.prototype.getSuperValue = function() {
    return this.property;
}

function SubType() {
    this.subproperty = false;
}

// 这里是关键,创建SuperType的实例,并将该实例赋值给SubType.prototype
SubType.prototype = new SuperType(); 

SubType.prototype.getSubValue = function() {
    return this.subproperty;
}

var instance = new SubType();
console.log(instance.getSuperValue()); // true

js常见的继承模式

基于原型链

不足:1. 子类生成的实例都共享一个对象(就是父类的一个实例对象);2.不能给超类的构造函数传参数

function SuperType(){
  this.name="i'm your father"
}
SubType.prototype=new SuperType()
SubType.prototype.constructor=SubType

借用构造函数(经典继承)

不足:超类原型上的方法不可见,不好复用,所以一般不单独使用

function SubType(args){
  SuperType.call(this,args)
}

组合继承(伪经典继承)

不足:调用了两次父类构造函数

function SubType(){
  //继承父类属性
  SuperType.call(this)
}
//继承父类方法
SubType.prototype=new SuperType()
SubType.prototype.constructor=SubType

原型式继承

指定一个对象作为原型创建

Object.create(protoObject/*指定对象*/)

寄生式继承

function create(protoObject){
  let clone=Object.create(protoObject/*指定对象*/)
  //增加方法
  clone.fn=function(){}
  return clone
}

寄生组合式继承

function inheritPrototype(subType,superType){
  // 以superType.prototype为原型
  // 使subType.prototype.__proto__=superType.prototype
  // 从而形成一个原型链来实现父类方法的继承
  subType.prototype=Object.create(superType.prototype)
  //将构造函数纠正为子类
  subType.prototype.constructor=subType
}

es6新特性

Object.getPrototypeOf()和Object.setPrototypeOf()

从 ECMAScript 6 开始,[[Prototype]] 可以通过 Object.getPrototypeOf() 和 Object.setPrototypeOf() 访问器来访问。这个等同于 JavaScript 的非标准但许多浏览器实现的属性 __proto__

class语法糖

class Person{
  static name
  //构造函数
  constructor(name){
    this.name=name
  }
  //原型方法
  say(){
    return `my name is ${this.name}`
  }
}
class Man extends Person{
  constructor(name){
    super(name)
    this.gender="man"
  }
  say(){
    return `i'm a ${this.gender}`
  }
}

extends与寄生组合式继承模式一样,作用如下:

  1. super(this)借用父类的构造函数,默认隐式执行,如果子类需要扩展,则需要先显示执行。
  2. Man.prototype.__proto__ = Person.prototype通过将原型对象指向父类原型对象,来继承父类的可继承属性和方法
  3. Man.prototype.constructor = Man将构造函数纠正为子类本身

结语

以上是我个人对 js继承的理解,如有错误,请指正。