ES6 类 class

最后更新:
阅读次数:

定义 class

  • 使用 class 关键字定义一个类
class Circle {
constructor(r) {
this.r = r;
}

getArea() {
return Math.PI * this.r ** 2;
}
}

console.log(typeof Circle); // function

// 新建一个实例
let c1 = new Circle(3);
c1.getArea(); // 28.274333882308138

console.log(Object.prototype.toString.call(c1)); // [object Object]
  • 类名指向构造函数
    • constructor 方法是类的构造函数,如果没显式定义,则一个空的 constructor 方法会被默认添加。
Circle === Circle.prototype.constructor; // true
Circle === c1.constructor; // true
  • class 不存在变量提升(hoist)
let a = new Circle(); // 报错,Uncaught ReferenceError: Circle is not defined

class Circle {
// ...
}
  • 与函数一样,class 也有 class 表达式
const MyClass = class {
/* ... */
};
  • 事实上,类的所有方法都定义在类的 prototype 属性上面
Circle.prototype.getArea === c1.getArea; // true
  • 类的内部所有定义的方法,都是不可枚举的(non-enumerable)
Object.keys(Circle.prototype); // []
Object.getOwnPropertyNames(Circle.prototype); // ["constructor", "getArea"]
  • class 内部属性的取值函数(getter)和存值函数(setter)
class Person {
constructor(name) {
this.name = name;
}

get name() {
return "this.name";
}

set name(name) {
name = "改不了";
console.log(name);
}
}

let p = new Person("Celeste");

p.name; // this.name
p.name = "hehe"; // 打印 '改不了'
p.name; // this.name

class 的属性

以下 class 属性的语法还处于提案中,浏览器尚不支持

  • 类的实例属性
class A {
instanceProp = 123;
}

let a = new A();
console.log(a.instanceProp);
  • 类的静态属性: 用 static 关键字声明
class A {
static staticProp = 123;
}

console.log(A.staticProp);

let a = new A();
console.log(a.staticProp); // 报错或 undefined
  • 类的私有属性: 在属性名之前,添加 # 符号,表示私有属性
class A {
#privateProp = 123;

constructor() {
this.prop = #privateProp;
}
}

let a = new A();
console.log(a.prop);

ES6 明确规定,class 内部只有静态方法,没有静态属性。以后的语法会不会支持静态属性,静待啦!

class 的静态方法

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上 static 关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就是静态方法

  • 类的静态方法不会被实例继承
class A {
static hello() {
console.log("hello world");
}
}

A.hello(); // 打印 hello world

let a = new A();
a.hello(); // 报错,Uncaught TypeError: a.hello is not a function
  • 父类的静态方法,可以被子类继承
    • 父类的静态方法也可以从 super 对象上在子类静态方法中调用
class A {
static hello() {
console.log("hello world");
}
}

class B extends A {
static greet() {
super.hello(); // 利用 super 调用父类的静态方法
}
}

A.hello(); // 打印 hello world
B.hello(); // 打印 hello world

B.greet(); // 打印 hello world

类的继承

ES6 中,class 之间可以通过 extends 关键字实现继承。

  • super 关键字可用于调用父类的构造函数,也可用于调用父类的方法

  • 子类必须在构造函数中调用一次 super 方法,否则新建实例时会报错(Uncaught ReferenceError: this is not defined)。这是因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类就得不到 this 对象。

class Animal {
constructor(kind) {
this.kind = kind;
}
running() {
console.log("I can run~");
}

eatting() {
console.log("I can eat~");
}
}

class Dog extends Animal {
constructor(name) {
super("Dog"); // 调用父类的构造函数
this.name = name;
}

speaking() {
console.log("汪汪汪~");
super.running(); // 调用父类的方法
}
}

let dog = new Dog("小八");
dog.speaking();

// 汪汪汪~
// I can run~
  • 子类的实例也是父类的实例
dog instanceof Dog; // true
dog instanceof Animal; // true
dog instanceof Object; // true
  • 子类与父类的 prototype 属性和 __proto__ 属性的关系
class A {}
class B extends A {}

B.__proto__ === A.prototype; // false
B.__proto__ === A; // true
B.prototype.__proto__ === A.prototype; // true

类与 new.target

  • 因为类必须通过 new 关键字调用,所以在类的构造函数中,new.target 属性永远不会是 undefined
// 在简单情况下,new.target 等于类的构造函数
class Animal {
constructor() {
// console.log(new.target);
console.log(new.target === Animal);
}
}

new Animal(); // 打印 true
// 下面的情况,new.target 不等于类的构造函数
class Animal {
constructor() {
console.log(new.target);
console.log(new.target === Animal);
}
}

class Dog extends Animal {
constructor() {
super();
}
}

new Dog(); // 打印 false

参考资料