用于定义类似对象之间的共享结构和行为的模板与蓝图
在 ES6 中,class (类)作为对象的模板被引入,可以通过 class 关键字定义类
class 的本质是 function
它可以看作一个语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法
class Example {
constructor(a) {
this.a = a;
}
}
class Example {}
class Example {}
// Uncaught SyntaxError: Identifier 'Example' has already been declared
注意,不可重复声明
类定义不会被提升,这意味着,必须在访问前对类进行定义,否则就会报错
类中方法不需要 function 关键字
方法间不能加分号
类表达式可以为匿名或命名
// 匿名类
let Example = class {
constructor(a) {
this.a = a;
}
};
// 命名类
let Example = class Example {
constructor(a) {
this.a = a;
}
};
ES6 中,prototype 仍存在,虽然可以直接自类中定义方法,但是其实方法还是定义在 prototype 上的。 覆盖方法 / 初始化时添加方法
Example.prototype = {
//methods
};
Object.assign(Example.prototype, {
//methods
});
Class 本身的属性,即直接定义在类内部的属性(Class.propname),不需要实例化。 ES6 中规定,Class 内部只有静态方法,没有静态属性。
class Example {
// 新提案
static a = 2;
}
// 目前可行写法
Example.b = 2;
class Example {}
Example.prototype.a = 2;
定义在实例对象(this)上的属性
class Example {
a = 2;
constructor() {
console.log(this.a);
}
}
返回跟在 class 后的类名(存在时)
let Example = class Exam {
constructor(a) {
this.a = a;
}
};
console.log(Example.name); // Exam
let Example = class {
constructor(a) {
this.a = a;
}
};
console.log(Example.name); // Example
类一定包含构造函数–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
console.log(new Example() instanceof Test); // true
class Example {
static sum(a, b) {
console.log(a + b);
}
}
Example.sum(1, 2); // 3
class Example {
sum(a, b) {
console.log(a + b);
}
}
let exam = new Example();
exam.sum(1, 2); // 3
class Example {
constructor() {
this.sum = (a, b) => {
console.log(a + b);
};
}
}
通过 new 关键字创建类的实例
class Example {}
let exam1 = Example();
// Class constructor Example cannot be invoked without 'new'
new 创建实例时都会执行构造函数
class Example {
constructor(a, b) {
this.a = a;
this.b = b;
console.log("Example");
}
sum() {
return this.a + this.b;
}
}
let exam1 = new Example(2, 1);
let exam2 = new Example(3, 1);
// __proto__ 已废弃,不建议使用
// console.log(exam1.__proto__ == exam2.__proto__);
console.log(Object.getPrototypeOf(exam1) === Object.getPrototypeOf(exam2)); // true
Object.getPrototypeOf(exam1).sub = function () {
return this.a - this.b;
};
console.log(Object.getPrototypeOf(exam1)); //{}
console.log(exam1.sub()); // 1
console.log(exam2.sub()); // 2
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)
@logMethod(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
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;
}
}
// 只输出 setter , 不会调用 getter 方法 递归
let exam1 = new Example1(1, 2);
console.log(exam1._a); // 1, 可以直接访问
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 # which has only a getter
需要初始化有效的类,所以必须有 set 方法
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
使用 extends 关键字继承父类,并再构造函数中调用 super(param)执行父类的构造函数,通过 extends 实现类的继承
class Child extends Father {}
子类 constructor 方法中必须有 super ,且必须出现在 this 之前
class Father {
constructor() {}
}
class Child extends Father {
constructor() {}
// or
// constructor(a) {
// this.a = a;
// super();
// }
}
let test = new Child(); // Uncaught ReferenceError: Must call super
// constructor in derived class before accessing 'this' or returning
// from derived constructor
调用父类构造函数,只能出现在子类的构造函数
class Father {
test() {
return 0;
}
static test1() {
return 1;
}
}
class Child extends Father {
constructor() {
super();
}
}
class Child1 extends Father {
test2() {
super(); // Uncaught SyntaxError: 'super' keyword unexpected
// here
}
}
调用父类方法, super 作为对象,在普通方法中,指向父类的原型对象,在静态方法中,指向父类
class Child2 extends Father {
constructor() {
super();
// 调用父类普通方法
console.log(super.test()); // 0
}
static test3() {
// 调用父类静态方法
return super.test1 + 2;
}
}
Child2.test3(); // 3
注意,不可继承常规对象
var Father = {
// ...
};
class Child extends Father {
// ...
}
// Uncaught TypeError: Class extends value #
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Welcome to check ${this.name} resource`);
}
}
class Author extends Person {
constructor(name, type) {
super(name);
this.type = type;
}
greet() {
console.log(`Welcome to check Author ${this.name} of ${this.type} resource`);
}
}
const peo = new Person("San mao");
const author1 = new Author("Lun xun", "Novel");
const author2 = new Author("Three", "Army");
peo.greet(); //Welcome to check San mao resource
author1.greet(); //Welcome to check Author Lun xun of Novel resource
author2.greet(); //Welcome to check Author Three of Army resource