文章目录

  • 前言
  • 一、es6中的类
  • 1、类的定义
  • 2、类的构造方法、方法定义和实现继承
  • 1、类的构造方法
  • 2、类的方法定义
  • 3、类实现继承
  • 二、es6转es5源码解析
  • 总结


前言

在前几节里面,介绍了在es6之前实现继承的几种方法,这些继承的方法都是基于原型和原型链的,写起来比较繁琐和麻烦,于是,在es6之后,推出了用class来定义类,实现继承,让我们来一起看看es6中关于类的相关知识吧!

一、es6中的类

1、类的定义

在ES6(ECMAScript2015)新的标准中使用了class关键字来直接定义类,但是类本质上依然是前面所讲的构造函数、原型链的语法糖而已,下面简单定义一个类并且添加一些属性和方法。

class Person{
	var name;
	var age;
	running() {
    console.log(this.name + " running~")
  }
}
console.log(Person.prototype.constructor)//class Person
console.log(typeof Person) // function
var p = new Person()
console.log(p.__proto__ === Person.prototype) // true

通过以上代码,你会发现类和我们的构造函数的特性其实基本上是一致的;

2、类的构造方法、方法定义和实现继承

1、类的构造方法

es6之前我们使用function来作为构造函数,参数可以通过括号传递进来,那在类中,如何实现传递参数呢?其实在类中我们想传递进来参数是通过构造函数constructor,具体如何实现,见下面代码:

// 类的声明
class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }
}


var p1 = new Person("a", 18)
var p2 = new Person("b", 30)
console.log(p1, p2)

注意: 一个类只能有一个构造函数
1.在内存中创建一个对象 moni = {}
2.将类的原型prototype赋值给创建出来的对象 moni.proto = Person.prototype
3.将对象赋值给函数的this: new绑定 this = moni
4.执行函数体中的代码
5.自动返回创建出来的对象

2、类的方法定义

普通的方法直接在类里面定义即可,通过创建的实例对象可以直接访问,静态方法在方法前面加static,可以通过类名来访问

var names = ["abc", "cba", "nba"]

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
    this._address = "广州市"
  }

  // 普通的实例方法
  // 创建出来的对象进行访问
  // var p = new Person()
  // p.eating()
  eating() {
    console.log(this.name + " eating~")
  }

  running() {
    console.log(this.name + " running~")
  }

  // 类的访问器方法
  get address() {
    console.log("拦截访问操作")
    return this._address
  }

  set address(newAddress) {
    console.log("拦截设置操作")
    this._address = newAddress
  }

  // 类的静态方法(类方法)
  // Person.createPerson()
  static randomPerson() {
    var nameIndex = Math.floor(Math.random() * names.length)
    var name = names[nameIndex]
    var age = Math.floor(Math.random() * 100)
    return new Person(name, age)
  }
}

var p = new Person("why", 18)
p.eating()
p.running()

console.log(p.address)
p.address = "北京市"
console.log(p.address)

// console.log(Object.getOwnPropertyDescriptors(Person.prototype))

for (var i = 0; i < 50; i++) {
  console.log(Person.randomPerson())
}

3、类实现继承

通过extends关键字可以实现继承,

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  running() {
    console.log(this.name + " running~")
  }

  eating() {
    console.log(this.name + " eating~")
  }

  personMethod() {
    console.log("处理逻辑1")
    console.log("处理逻辑2")
    console.log("处理逻辑3")
  }

  static staticMethod() {
    console.log("PersonStaticMethod")
  }
}

// Student称之为子类(派生类)
class Student extends Person {
  // JS引擎在解析子类的时候就有要求, 如果我们有实现继承
  // 那么子类的构造方法中, 在使用this之前
  constructor(name, age, sno) {
    super(name, age)
    this.sno = sno
  }

  studying() {
    console.log(this.name + " studying~")
  }

  // 类对父类的方法的重写
  running() {
    console.log("student " + this.name + " running")
  }

  // 重写personMethod方法
  personMethod() {
    // 复用父类中的处理逻辑
    super.personMethod()

    console.log("处理逻辑4")
    console.log("处理逻辑5")
    console.log("处理逻辑6")
  }

  // 重写静态方法
  static staticMethod() {
    super.staticMethod()
    console.log("StudentStaticMethod")
  }
}

var stu = new Student("why", 18, 111)
console.log(stu)

// console.log(Object.getOwnPropertyDescriptors(stu.__proto__))
// console.log(Object.getOwnPropertyDescriptors(stu.__proto__.__proto__))

stu.eating()
stu.running()

stu.personMethod()

Student.staticMethod()

console.log(Object.getOwnPropertyDescriptors(Person))

继承后,子类中可获得父类的方法,通过super关键字可向父类构造函数传递参数,也可以通过super获得父类中的方法并调用它。

二、es6转es5源码解析

其实,es6中的类只是一种语法糖,最后还是要通过babel转换成es5的代码,想知道转换后的代码,我们可以访问官方网站:babeljs.com,可以看到转换之后的代码。
以下是源码:

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  eating() {
    console.log(this.name + " eating~")
  }
}

转换后的代码:

// babel转换
"use strict";

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

// /*#__PURE__*/ 纯函数
// webpack 压缩 tree-shaking
// 这个函数没副作用
var Person = /*#__PURE__*/ (function () {
  function Person(name, age) {
   _classCallCheck(this, Person);
    this.name = name;
    this.age = age;
  }

  _createClass(Person, [
    {
      key: "eating",
      value: function eating() {
        console.log(this.name + " eating~");
      }
    }
  ]);

  return Person;
})();

_classCallCheck函数首先检测创建出来的是否是Person的实例,如果不是的话, throw new TypeError(“Cannot call a class as a function”);

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

_createClass函数:如果传进来的不是静态方法,调用_defineProperties函数,穿进去第一个参数是Person.prototype,也就是给erson.prototype.eating=function(){},如果是,则传进去的是Person,那么eating这个函数直接加在了Person对象里面。

_createClass(Person, [
    {
      key: "eating",
      value: function eating() {
        console.log(this.name + " eating~");
      }
    }
  ]);
function _createClass(Constructor, protoProps, staticProps) {
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  if (staticProps) _defineProperties(Constructor, staticProps);
  return Constructor;
}

_defineProperties函数:遍历props里面的每个属性,并且给Person设置属性描述符,

function _defineProperties(target, props) {
  for (var i = 0; i < props.length; i++) {
    var descriptor = props[i];
    descriptor.enumerable = descriptor.enumerable || false;
    descriptor.configurable = true;
    if ("value" in descriptor) descriptor.writable = true;
    Object.defineProperty(target, descriptor.key, descriptor);
  }
}

总结

es6对象利用class关键字创建,通过constructor构造函数传递参数,子类通过extends关键字继承,子类通过super关键字可向父类构造函数传递参数或者调用父类里面的方法。