es6 class

对于面向对象的编程语言这一说法,准确来说应该是 支持使用面向对象程序设计思想进行编程的编程语言,而这一思想的本质其实是抽象。从这点出发,其实 JavaScript 就是一门面向对象的语言,但是其他面向对象的语言(比如 Java、C++)是基于 class 来实现的,而 JavaScript 是基于原型来实现”

class

es6 新增了 class,下面创建一个关于 Animal 的类

class Animal {
  constructor(name) {
    // 这里面的this会指向实例
    this.name = name;
  }
  static parentName = "Animal"; // 静态属性
  static getParentName() {
    // 1.静态方法的this只会指向父类
    // 2.可以通过父类调用
    // 3.静态方法不能被实例继承,但可以被子类继承
    console.log(this.parentName);
    return this.parentName;
  }
  getName() {
    console.log(this.name);
    return this.name;
  }
}

实现 class

其实 es6 的 class 就是一个语法糖,是基于 js 构造函数和原型去实现的,实现代码如下:

function createClass(Constructor, protoProps, staticProps) {
  if (protoProps) {
    Object.defineProperties(Constructor.prototype, protoProps);
  }
  if (staticProps) {
    Object.defineProperties(Constructor, staticProps);
  }
  return Constructor;
}
function Animal(name) {
  // 实例属性赋值
  this.name = name;
}

// 在构造函数的原型对象上挂载实例方法
createClass(
  Animal,
  {
    getName: {
      value: function getName() {
        console.log("调用实例方法,获取实例属性", this.name);
        return this.name;
      },
    },
  },
  {
    getParentName: {
      // 在构造函数上挂载静态属性和方法
      value: function getParentName() {
        console.log("调用静态方法,获取静态属性", this.parentName);
        return this.parentName;
      },
    },
    parentName: {
      value: "Animal",
    },
  }
);

let animal1 = new Animal("animal1");

// 调用实例方法
animal1.getName();
// 调用静态方法
Animal.getParentName();

在创建对象实例时 let animal1 = new Animal('animal1'),主要会经历几个步骤

// 1.新建空对象
let newObj = new Object();
// 2.指向构造函数的原型对象,共享实例方法
newObj.__proto__ = Animal.prototype;
// 3.以新对象为上下文执行构造函数
let result = Animal.call(newObj, "animal1");
// 4.返回一个新对象
// 如果构造函数返回一个对象或函数,对象实例就是该对象或函数;否则返回一个新对象
return result instanceof Object ? result : newObj;

继承

下面通过 extends 关键字实现对 Animal 的继承

class Animal {
  constructor(name) {
    // 实例属性赋值,这里面的this会指向实例
    this.name = name;
  }
  static parentName = "Animal"; // 静态属性
  getName() {
    console.log("调用实例方法,获取实例属性", this.name);
    return this.name;
  }
}

class Cat extends Animal {
  constructor(name) {
    super(name);
  }
}

let cat = new Cat("jack");
cat.getName();
console.log(Cat.parentName);
// 可以发现,静态属性和方法也被子类继承了

实现 extends

extends 本质上是基于寄生式组合继承来实现的

  1. new 一个实例时,调用父类构造函数,继承实例属性
  2. 通过原型链继承父类实例方法
  3. 设置子类的 proto = 父类,让子类可以调用父类静态属性和方法
// 已知 Animal 这个父类
function _inherits(child, parent) {
  let proto = Object.create(parent.prototype);
  proto.constructor = child;
  child.prototype = proto;
}

function Cat(name) {
  // 调用父类的构造函数,继承实例属性
  Animal.call(this, name);
}

// 继承父类实例方法
_inherits(Cat, Animal);
// 设置子类的 __proto__ = 父类,让子类可以调用父类静态属性和方法
Object.setPrototypeOf(Cat, Animal);

let cat = new Cat("jack");
cat.getName();
console.log(Cat.parentName);