zengyj
Articles5
Tags2
Categories0
讲一讲ES6中的类

讲一讲ES6中的类

下面讲一讲ES6引入的类

1
2
3
4
5
6
7
8
9
10
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}

toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}

上述代码中定义了一个类,constructor代表构造函数,上述还定义了一个toString()方法,前面不需要加上function这个关键字,另外,方法与方法之间不需要逗号分隔,加了会报错。

1
const p = new Point();

使用时与构造函数用法无异。

另,注意,类中的方法都是不可枚举的,例如下面代码

1
2
Object.keys(Point.prototype)
// []

而如果采用ES5写法,则是可枚举的

constructor()方法默认返回实例对象(即this),完全可以指定返回另外一个对象。

我们也可在类中对某个属性设置存值函数和取值函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Point {
constructor(x, y) {
//...
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
get prop() {
return 'getter';
}
set prop(value) {
console.log('setter: '+value);
}
}

另,如果某个方法之前加上星号(*),就表示该方法是一个 Generator 函数。

补充知识:

Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

1
2
3
4
function* gen(x){
var y = yield x + 2; //表示异步需要暂停的地方
return y;
}
1
2
3
var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }

上面代码中,调用 Generator 函数,会返回一个内部指针(即遍历器 )g 。这是 Generator 函数不同于普通函数的另一个地方,即执行它不会返回结果,返回的是指针对象。每次调用 next 方法,会返回一个对象,表示当前阶段的信息( value 属性和 done 属性)。value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。

静态方法

顾名思义,在方法前加static,表示该方法不会被实例对象所继承,直接通过类调用。静态方法可以与非静态方法重名。父类的静态方法,可以被子类继承。

实例属性

类的实例属性有两种定义方式,如下面代码:

1
2
3
4
5
6
7
8
9
10
//方法一
class IncreasingCounter {
constructor() {
this._count = 0;
}
}
//方法二
class IncreasingCounter {
_count = 0;
}

静态属性

类的静态属性方法有两种定义方式,如下面代码:

1
2
3
4
5
6
7
8
9
// 方法一
class Foo {
// ...
}
Foo.prop = 1;
// 方法二
class Foo {
static prop = 1;
}

私有方法与私有属性

类的私有方法方法有两种定义方式,如下面代码:

1
//方法一:加入下划线形式class Widget {  _bar(baz) {    return this.snaf = baz;  }}//方法二:将私有方法移出类class Widget {  foo (baz) {    bar.call(this, baz);  }}function bar(baz) {  return this.snaf = baz;}

私有属性的定义如下:

1
class IncreasingCounter {  #count = 0;  //也可用来定义私有方法  #sum() {    return this.#a + this.#b;  }}

in运算符

1
class A {  use(obj) {    if (#foo in obj) {      // 私有属性 #foo 存在    } else {      // 私有属性 #foo 不存在    }  }}

new.target属性

属性一般用在构造函数之中,返回new命令作用于的那个构造函数。如果构造函数不是通过new命令或Reflect.construct()调用的,new.target会返回undefined。

1
function Person(name) {  if (new.target === Person) {    this.name = name;  } else {    throw new Error('必须使用 new 命令生成实例');  }}var person = new Person('张三'); // 正确var notAPerson = Person.call(person, '张三');  // 报错

继承

类通过”extends“来继承,如下面代码:

1
class Point {}class ColorPoint extends Point {}

注意,子类的构造函数必须先调用父类的构造函数才能再进行自我加工。

1
class ColorPoint extends Point {  constructor(x, y, color) {    super(x, y); // 调用父类的constructor(x, y)    this.color = color;  }  toString() {    return this.color + ' ' + super.toString(); // 调用父类的toString()  }}

需要注意的是,父类的静态方法也会被子类所继承。

super

super一方面充当父类的构造函数,另一方面当对象使用,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

注意:如果在子类中使用super对某个属性进行赋值,此时super会化身this

1
class A {  constructor() {    this.x = 1;  }}class B extends A {  constructor() {    super();    this.x = 2;    super.x = 3;    console.log(super.x); // undefined    console.log(this.x); // 3  }}

老生常谈的prototype和__proto__

在class中,它同时拥有prototype属性和_proto_属性,其中

子类的__proto__属性,表示构造函数的继承,总是指向父类

子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。

正是因为上述规则,类的继承是按如下代码实现的。

1
class A {}class B {}// B 的实例继承 A 的实例Object.setPrototypeOf(B.prototype, A.prototype);// B 继承 A 的静态属性Object.setPrototypeOf(B, A);const b = new B();
Author:zengyj
Link:http://example.com/2021/05/20/ES6%E7%B1%BB/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可