「前端开发」 -

JS-ES6-10

ECMAScript6 —— Class

Posted by eliochiu on November 16, 2022

简介

JavaScript语言传统方法书通过构造函数生成新对象:

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

Point.prototype.toString = function() {
  return '(' + this.x + ',' + this.y + ')';
}

var p = new Point(1, 2)

上面这种写法和传统面向对象语言C++/Java写法差异很大,因此ES6提供了接近传统语言的写法,引入了Class作为对象的模版,通过class关键字可以定义类。基本上,ES6 中的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程的语法而己。

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 + ')';
  }
}

类与原型

ES6的类完全可以看成构造函数的另一种写法。

1
2
3
4
5
6
class Point {
  // ...
}

typeof Point // "function"
Point === Point.prototype.constructor; // true

使用的时候也是直接使用new关键字创建实例。构造函数的prototype属性在ES6的“类”上继续存在。事实上,类的所有方法都定义在类的prototype属性上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Point {
  constructor() {
    //...
  }

  toString() {
    // ...
  }

  toValue() {
    //...
  }
}

// 等价于
Point.prototype = {
  constructor() {},
  toString() {},
  toValue() {}
}

在类的实例上调用方法,实际上调用的就是原型上的方法。

1
2
const p = new Point(1, 2);
console.log(p.toString === Point.prototype.toString); // true

类上定义的所有方法都是不可枚举的(non-enumberable),也就是说他们不能被Object.keys访问到。

1
Object.keys(Ponit.prototype); // []

Class表达式

与函数一样,Class也可以使用表达式的形式定义。

1
2
3
const myClass = class Me {
  // ...
}

注意,这个类的名字是myClass,而不是Me

也可以写成匿名的类:

1
2
3
const myClass = class {
  // ...
}

类表达式可以创建立刻执行的类:

1
2
3
4
5
6
7
8
9
10
11
let person = new class {
  constructor(name) {
    this.name = name;
  }

  sayName() {
    console.log(this.name);
  }
}('张三');

person.sayName(); // 张三

不存在变量提升

类不存在变量提升。

1
2
new Foo();
class Foo {}; // ReferenceError

私有方法

私有方法是常见需求,但ES6不支持,只能通过变通方法模拟来实现:

命名区分

1
2
3
4
5
6
7
8
9
10
11
12
13
class Widget {
  // 共有方法
  foo(baz) {
    this._bar(baz);
  }

  // 私有方法
  _bar(baz) {
    return this.snaf = baz;
  }

  // ...
}

划线部分表示这是一个仅仅用在内部的私有方法,但是这种命名并不保险,在类的外部仍然可以调用。

移出类

另一种方法是将私有方法移出模块,因为模块内部的所有方法都是对外可见的。

1
2
3
4
5
6
7
8
9
class Widget {
  foo(baz) {
    bar.call(this, baz)
  }
}

function bar(baz) {
  return this.snaf = baz;
}

Symbol

还有一种方法,将私有方法命名为一个Symbol值。由于其唯一性,第三方无法获取到他们,达到了私有属性和私有方法的目的:

1
2
3
4
5
6
7
8
9
10
11
12
13
const bar = Symbol("bar");
const snaf = Symbol("snaf");

class myClass {
  // 共有方法
  foo(baz) {
    this[bar](baz);
  }

  [bar](baz) {
    return this[snaf] = baz;
  }
}

getter&setter

和ES5一样,在类的内部可以使用getset关键字对某个属性设置取值函数和存取函数,拦截属性的存取行为。他们定义在属性的Descriptor上。 ```js class myClass { constructor() { // … } get prop() { return “getter” }

set prop(value) { console.log(‘setter’ + value); } }

let inst = new MyClass(); inst.prop = 123; // setter: 123

inst.prop; // getter

静态

静态方法

静态属性