ES6深入—ES6 Class 类


前言

本文会简要介绍 ES6 Class 类的知识,如何定义如何使用,通过本节实验,需要熟练掌握 ES6 当中类的定义和使用。

一、基础用法

类定义

类表达式可以是匿名或命名的方式。

// 匿名类
let ExampleA = class {
    
    
  constructor(count) {
    
    
    this.count = count;
  }
};
// 命名类
let ExampleB = class ExampleB {
    
    
  constructor(a) {
    
    
    this.a = a;
  }
};

类声明

class ExampleC {
    
    
  constructor(a) {
    
    
    this.a = a;
  }
}
  • 注意要点:不可以重复声明,不然报错。
class ExampleA {
    
    }
class ExampleA {
    
    }
// Uncaught SyntaxError: Identifier 'ExampleA' has already been
// declared

let Example1 = class {
    
    };
class ExampleA {
    
    }
// Uncaught SyntaxError: Identifier 'Example' has already been
// declared
  • 注意:必须在访问前对类进行定义,否则就会报错。类中方法不需要 function 关键字。方法间不能加分号。
new Example();
class Example {}

步骤如下:

  1. 创建名称为 test1.js 的文件,并且输入下面的代码。
// 匿名类
let ExampleA = class {
    
    
  constructor(count) {
    
    
    this.count = count;
  }
};
// 命名类
let ExampleB = class ExampleB {
    
    
  constructor(a) {
    
    
    this.a = a;
  }
};
console.log(ExampleA);
console.log(ExampleB);
  1. 在终端运行,结果如下所示:

请添加图片描述

二、类的主体

属性 prototype

ES6 中,prototype 仍旧存在,虽然可以直接在类中定义方法,但是其实方法还是定义在 prototype 上的。覆盖方法 / 初始化时添加方法如下:

ExampleA.prototype = {
    
    
  // methods
};

添加方法:

Object.assign(ExampleA.prototype, {
    
    
  // methods
});

静态属性

静态属性:class 本身的属性,也就是直接定义在类内部的属性( Class.propname ),不需要实例化。ES6 中规定,Class 内部只有静态方法,没有静态属性。

class ExampleD {
    
    
  a = 2;
  constructor() {
    
    
    console.log(this.a);
  }
}

name 属性

返回跟在 class 后的类名(存在时)。

let ExampleE = class Exam {
    
    
  constructor(a) {
    
    
    this.a = a;
  }
};
console.log(ExampleE.name); // Exam

let ExampleF = class {
    
    
  constructor(a) {
    
    
    this.a = a;
  }
};
console.log(ExampleF.name); // ExampleF

实例:

创建名称为 test2.js 的文件,并且输入下面的代码。

// 静态属性

class ExampleA {
    
    
  // 新提案
  static a = 3;
}
// 目前可行写法
ExampleA.b = 3;

// 公共属性
class ExampleB {
    
    }
ExampleB.prototype.a = 2;
// 实例属性
class ExampleC {
    
    
  a = 2;
  constructor() {
    
    
    console.log(this.a);
  }
}

// name 属性
let ExampleD = class ExamD {
    
    
  constructor(a) {
    
    
    this.a = a;
  }
};
console.log(ExampleD.name); // Exam

let ExampleE = class {
    
    
  constructor(a) {
    
    
    this.a = a;
  }
};
console.log(ExampleE.name); // Example

在终端运行,结果如下所示:
请添加图片描述

三、方法

constructor 方法

constructor 方法是类的默认的方法,创建类的实例化对象时被调用。

class Example {
    
    
  constructor() {
    
    
    console.log("我是constructor");
  }
}
new Example(); // 我是 constructor

返回对象

class Test {
    
    
  constructor() {
    
    
    // 默认返回实例对象 this
  }
}
console.log(new Test() instanceof Test); // true

class Example {
    
    
  constructor() {
    
    
    // 指定返回对象
    return new Test();
  }
}
console.log(new Example() instanceof Example); // false

静态方法

class Example {
    
    
  static sum(a, b) {
    
    
    console.log(a + b);
  }
}
Example.sum(1, 2); // 3

原型方法

class Example {
    
    
  constructor() {
    
    
    this.sum = (a, b) => {
    
    
      console.log(a + b);
    };
  }
}

实例方法

class Example {
    
    
  constructor() {
    
    
    this.sum = (a, b) => {
    
    
      console.log(a + b);
    };
  }
}

实例:

创建名称为 test3.js 的文件,并且输入下面的代码。

// constructor 方法

class Example {
    
    
  constructor() {
    
    
    console.log("我是constructor");
  }
}
new Example(); // 我是 constructor

// 返回对象

class Test {
    
    
  constructor() {
    
    
    // 默认返回实例对象 this
  }
}
console.log(new Test() instanceof Test); // true

class ExampleB {
    
    
  constructor() {
    
    
    // 指定返回对象
    return new Test();
  }
}
console.log(new Example() instanceof ExampleB); // false

// 静态方法

class ExampleD {
    
    
  static sum(a, b) {
    
    
    console.log(a + b);
  }
}
ExampleD.sum(1, 2); // 3

// 原型方法
class ExampleE {
    
    
  constructor() {
    
    
    this.sum = (a, b) => {
    
    
      console.log(a + b);
    };
  }
}

// 实例方法
class ExampleF {
    
    
  constructor() {
    
    
    this.sum = (a, b) => {
    
    
      console.log(a + b);
    };
  }
}

在终端运行,结果如下所示:
请添加图片描述

四、类的实例化

new

类的实例化,必须通过 new 关键字。

class ExampleG {
    
    }

let exam1 = ExampleG();
// Class constructor Example cannot be invoked without 'new'

实例化对象

共享原型对象。

class ExampleH {
    
    
  constructor(m, n) {
    
    
    this.m = m;
    this.n = n;
    console.log("ExampleH");
  }
  sum() {
    
    
    return this.m + this.n;
  }
}
let exam1 = new ExampleH(5, 3);
let exam2 = new ExampleH(2, 7);
console.log(exam1._proto_ == exam2._proto_); // true

exam1.__proto__.sub = function () {
    
    
  return this.a - this.b;
};
console.log(exam1.sum()); // 8
console.log(exam2.sum()); // 9

实例:

创建名称为 test4.js 的文件,并且输入下面的代码。

class ExampleH {
    
    
  constructor(m, n) {
    
    
    this.m = m;
    this.n = n;
    console.log("ExampleH");
  }
  sum() {
    
    
    return this.m + this.n;
  }
}
let exam1 = new ExampleH(5, 3);
let exam2 = new ExampleH(2, 7);
console.log(exam1._proto_ == exam2._proto_); // true

exam1.__proto__.sub = function () {
    
    
  return this.a - this.b;
};
console.log(exam1.sum()); // 8
console.log(exam2.sum()); // 9

在终端运行,结果如下所示:
请添加图片描述

五、decorator

decorator 是一个函数,用来修改类的行为,在代码编译时产生作用。

类修饰

一个参数。

第一个参数 target,指向类本身。

function testable(target) {
    
    
  target.isTestable = true;
}
@testable
class Example {
    
    }
Example.isTestable; // true

多个参数——嵌套实现。

function testable(isTestable) {
    
    
  return function (target) {
    
    
    target.isTestable = isTestable;
  };
}
@testable(true)
class Example {
    
    }
Example.isTestable; // true

实例属性,上面两个例子添加的是静态属性,若要添加实例属性,在类的 prototype 上操作即可。

方法修饰
3 个参数:target(类的原型对象)、name(修饰的属性名)、descriptor(该属性的描述对象)。

class Example {
    
    
  @writable
  sum(a, b) {
    
    
    return a + b;
  }
}
function writable(target, name, descriptor) {
    
    
  descriptor.writable = false;
  return descriptor; // 必须返回
}

修饰器执行顺序

由外向内进入,由内向外执行。

class Example {
    
    
  @logMethod(1)
  @logMthod(2)
  sum(a, b) {
    
    
    return a + b;
  }
}
function logMethod(id) {
    
    
  console.log("evaluated logMethod" + id);
  return (target, name, desctiptor) =>
    console.log("excuted         logMethod " + id);
}
// evaluated logMethod 1
// evaluated logMethod 2
// excuted logMethod 2
// excuted logMethod 1

六、封装与继承

getter / setter

定义如下所示:

class Example {
    
    
  constructor(a, b) {
    
    
    this.a = a; // 实例化时调用 set 方法
    this.b = b;
  }
  get a() {
    
    
    console.log("getter");
    return this.a;
  }
  set a(a) {
    
    
    console.log("setter");
    this.a = a; // 自身递归调用
  }
}
let exam = new Example(1, 2); // 不断输出 setter,最终导致 RangeError
class Example1 {
    
    
  constructor(a, b) {
    
    
    this.a = a;
    this.b = b;
  }
  get a() {
    
    
    console.log("getter");
    return this._a;
  }
  set a(a) {
    
    
    console.log("setter");
    this._a = a;
  }
}
let exam1 = new Example1(1, 2); // 只输出 setter, 不会调用 getter 方法
console.log(exam._a); // 1, 可以直接访问

创建 名称为 test5.js 的文件,并且输入下面的代码。

class Example {
    
    
  constructor(a, b) {
    
    
    this.a = a; // 实例化时调用 set 方法
    this.b = b;
  }
  get a() {
    
    
    console.log("getter");
    return this.a;
  }
  set a(a) {
    
    
    console.log("setter");
    this.a = a; // 自身递归调用
  }
}
let exam = new Example(1, 2); // 不断输出 setter,最终导致 RangeError
class Example1 {
    
    
  constructor(a, b) {
    
    
    this.a = a;
    this.b = b;
  }
  get a() {
    
    
    console.log("getter");
    return this._a;
  }
  set a(a) {
    
    
    console.log("setter");
    this._a = a;
  }
}
let exam1 = new Example1(1, 2); // 只输出 setter, 不会调用 getter 方法
console.log(exam._a); // 1, 可以直接访问

在终端输入 node test5.js 运行,结果如下所示:
请添加图片描述

  • 特殊情况:getter 不可单独出现。
class Example {
    
    
  constructor(a) {
    
    
    this.a = a;
  }
  get a() {
    
    
    return this.a;
  }
}
let exam = new Example(1); // Uncaught TypeError: Cannot set property // a of #<Example> which has only a getter
  • 要点:getter 与 setter 必须同级出现。
class Father {
    
    
  constructor() {
    
    }
  get a() {
    
    
    return this._a;
  }
}
class Child extends Father {
    
    
  constructor() {
    
    
    super();
  }
  set a(a) {
    
    
    this._a = a;
  }
}
let test = new Child();
test.a = 2;
console.log(test.a); // undefined

class Father1 {
    
    
  constructor() {
    
    }
  // 或者都放在子类中
  get a() {
    
    
    return this._a;
  }
  set a(a) {
    
    
    this._a = a;
  }
}
class Child1 extends Father1 {
    
    
  constructor() {
    
    
    super();
  }
}
let test1 = new Child1();
test1.a = 2;
console.log(test1.a); // 2

创建名称为 test6.js 的文件,并且输入上面的代码。

在终端输入 node test6.js 后运行,结果如下所示:
请添加图片描述
extends
通过 extends 实现类的继承。

class Child extends Father {
    
     ... }

七、super

子类 constructor 方法中必须有 super,且必须出现在 this 之前,下面的代码就是没有 super 函数导致出错的情况。

class Father {
  constructor() {}
}
class Child extends Father {
  constructor() {}
  // or
  // constructor(a) {
  // this.a = a;
  // super();  // 这里必须要有,如果没有super()会报错
  // }
}
let test = new Child(); // Uncaught ReferenceError: Must call super
// constructor in derived class before accessing 'this' or returning
// from derived constructor

注意要点

不可继承常规对象。

var Father = {
    
    
  // ...
};
class Child extends Father {
    
    
  // ...
}
// Uncaught TypeError: Class extends value #<Object> is not a constructor or null

// 解决方案
Object.setPrototypeOf(Child.prototype, Father);

创建名称为 test7.js 的文件,并且输入下面的代码。


class Father {
    
    
  constructor() {
    
    }
}
class Child extends Father {
    
    
  constructor(a) {
    
    
    super();
    this.a = a;
    console.log(a); // print 12
  }
}
let test = new Child(12);
test;
在终端输入 node test7.js 运行,结果如下所示:
![请添加图片描述](https://img-blog.csdnimg.cn/4509b3f5e73e4cf197c4235c9d0a5406.png)
# 总结
本文介绍了 ES6 Class 的 ES6 类的定义及使用的 constructor() 方法、Class 表达式和 super 关键字。

下文讲解ES6 Generator 函数。



猜你喜欢

转载自blog.csdn.net/weixin_44009656/article/details/125039200