ES6中类的使用

认识class定义类

按照构造函数的形式创建类,不仅仅和编写普通的函数过于相似,而且代码并不容易理解

  • 在ES6(ECMAScript2015)新的标准中使用了class关键字来直接定义类
  • 但是类的本质上只是构造函数,原型链的语法糖
  • 最终还是会被 babel 工具转换为ES5的代码

定义类的方式

可以发现类和构造函数的特性是一致的

// 类的声明
class Person {

}

// 类的表达式(不怎么用)
var Student = class {

}

// 类的特性
console.log(Person) // [class Person]
console.log(Person.prototype);// {}
console.log(Person.prototype.__proto__); // [Object: null prototype] {}
console.log(Person.prototype.constructor); // [class Person]
console.log(typeof Person); // function

// 创建实例
var p = new Person()
console.log(p.__proto__ === Person.prototype); // true

类的构造方法

一个类只能有一个构造函数 constructor,如果包含多个会报错

new 的时候:

  1. 在内存中创建一个对象 moni = { }
  2. 将类的原型prototype赋值给创建出来的对象的[[prototype]],moni.__ptoto__ = Person.prototype
  3. 将对象复制给函数的this:new绑定 this = moni
  4. 执行函数体中的代码
  5. 自动返回创建出来的对象
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
}

var p1 = new Person("kac", 12)
var p2 = new Person("awe", 20)
console.log(p1, p2);
// Person { name: 'kac', age: 12 } Person { name: 'awe', age: 20 }

类中的方法定义

三种定义方法

  1. 普通的实例方法:实际上是放到原型上的,可以被多个实例共享

  2. 类的访问器方法:setter,getter

  3. 类的静态方法:直接使用类来调用的方法 Person.xxx(),不需要借助实例来调用,例如Promise.all(),也是类方法

    使用 static关键字来定义

var names = ["abc", "cba", "nba"]
class Person {
constructor(name, age) {
this.name = name
this.age = age
this._address = "广州市"
}

// 1.普通的实例方法
eating() {
console.log(this.name + " eating~");
}

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

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

// 3.类的静态方法(类方法):直接通过类来访问的方法
// Person.xxx()
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("axf", 18)
p.eating() // axf eating~

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

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

类的继承extends

ES6中新增了 extends 关键字,可以方便的帮助我们实现继承

super关键字,三个使用位置:子类的构造函数、实例方法、静态方法

  • 子类的构造函数:JS引擎在解析子类的时候就有要求,如果我们有实现继承,那么子类的构造方法中,在使用this之前,要调用super()

    否则报错

    ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
  • 子类可以重写父类的方法,在重写方法中使用 super 来复用父类中的逻辑

class Person {
constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log('逻辑1');
console.log('逻辑2');
console.log('逻辑3');
console.log(this.name + ' running~');
}
static staticMethod() {
console.log('person staticMethod');
}
}

class Student extends Person {
constructor(name, age, sno) {
// 在使用this前,这里必须使用super()
super(name, age)
this.sno = sno
}
// 重写方法一般实例方法
running() {
// 复用父类中的逻辑
super.running()
console.log('逻辑4');
console.log('逻辑5');
console.log('逻辑6');
console.log("student " + this.name + " running");
}
// 重写父类中的静态方法
static staticMethod() {
super.staticMethod()
console.log('student staticMethod');
}
}

let p = new Student("asx", 12, 111)
console.log(p);
// p.running()
Student.staticMethod()

继承内置类

让我们的类继承内置类,比如:Array

这样我们就可以得到Array的方法了

class MyArray extends Array{
firstItem() {
return this[0]
}

lastItem() {
return this[this.length - 1]
}
}

var arr = new MyArray(1,2,3)
console.log(arr.firstItem());
console.log(arr.concat([33])) // MyArray(4) [ 1, 2, 3, 33 ]

类的混入(少用)

JavaScript 的类只支持单继承,也就是只能有一个父类

如果想在一个类中添加更多相似的功能时,可以使用混入

class Person {}

// 通过函数去继承一些公共方法
function mixinRunner(BaseClass) {
class NewClass extends BaseClass {
running() {
console.log('I am running~');
}
}
return NewClass
}

function mixinEater(BaseClass) {
class NewClass extends BaseClass {
eating() {
console.log('I am eating~');
}
}
return NewClass
}

// 继承父类
class Student extends Person {}

// var NewStudent = mixinRunner(Student) // 返回一个已经继承了run方法的新的类
// 在继承了run的基础上继续继承eat
var NewStudent = mixinEater(mixinRunner(Student))

var stu = new NewStudent()
stu.running()
stu.eating()

JavaScript中的多态

维基百科:多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口,或使用一 个单一的符号来表示多个不同的类型

总结:不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现

所以说,JS也是有多态的,虽然跟别的一些面向对象语言有些区别

function calcArea(foo) {
console.log(foo.getArea());
}

var obj1 = {
name: "sfr",
getArea: function() {
return 1000
}
}

class Person {
getArea() {
return 100
}
}

var p = new Person()

calcArea(obj1)
calcArea(p)

// 以下也是多态的体现
function sum(m, n) {
return m + n
}

sum(20, 30) // 不同数据类型,不同形态
sum("abc", "asf")

ES6转ES5

可以在 babeljs.io 中看es6通过babel转es5后的代码

有点难度,不要浮躁,慢慢看

可以打 debug 一点点看

例如:

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");
}
}

// target: Person.prototype
// props: [{key, value}]
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);
}
}

// Constructor: Person
// protoProps: [{key, value}]
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) {
this.name = name;
this.age = age;
}

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

return Person;
})();