1.类的简介
JavaScript 语言中,生成实例对象的传统方法是通过构造函数。下面是一个例子。
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);
上面的代码用 ES6 的class
改写,就是下面这样。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
上面的constructor
方法,称为构造方法,- this代表实例对象
- ES5 的构造函数
Point
,对应 ES6 的Point
类的构造方法。 - 类的所有方法都定义在类的
prototype
属性上面。 - 类必须使用
new
调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用new
也可以执行。
类的所有方法都定义在类的prototype
属性上面。
class Point {
constructor() {
// ...
}
toString() {
// ...
}
toValue() {
// ...
}
}
// 等同于
Point.prototype = {
constructor() {},
toString() {},
toValue() {},
};
在类的实例上面调用方法,其实就是调用原型上的方法。b
是B
类的实例,它的constructor
方法就是B
类原型的constructor
方法。
class B {}
let b = new B();
b.constructor === B.prototype.constructor // true
与 ES5 一样,实例的属性除非显式定义在其本身(即定义在this
对象上),否则都是定义在原型上(即定义在class
上)。
//定义类
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var point = new Point(2, 3);
point.toString() // (2, 3)
point.hasOwnProperty('x') // true
point.hasOwnProperty('y') // true
point.hasOwnProperty('toString') // false
point.__proto__.hasOwnProperty('toString') // true
类的所有实例共享一个原型对象。p1
和p2
都是Point
的实例,它们的原型都是Point.prototype
,所以__proto__
属性是相等的。
var p1 = new Point(2,3);
var p2 = new Point(3,2);
p1.__proto__ === p2.__proto__
//true
类的方法内部如果含有this
,它默认指向类的实例。
现在回忆一下原型链
2.静态方法(类的方法。加static)
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static
关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
Foo
类的classMethod
方法前有static
关键字,表明该方法是一个静态方法,可以直接在Foo
类上调用(Foo.classMethod()
),而不是在Foo
类的实例上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
注意,如果静态方法包含this
关键字,这个this
指的是类,而不是实例。
静态方法bar
调用了this.baz
,这里的this
指的是Foo
类,而不是Foo
的实例,等同于调用Foo.baz
。
class Foo {
static bar() {
this.baz();
}
static baz() {
console.log('hello');
}
baz() {
console.log('world');
}
}
Foo.bar() // hello
3.静态属性(类的属性,加static)
静态属性指的是 Class 本身的属性,即Class.propName
,而不是定义在实例对象(this
)上的属性。
因为 ES6 明确规定,Class 内部只有静态方法,没有静态属性。
现在有一个提案提供了类的静态属性,写法是在实例属性法的前面,加上static
关键字。
class MyClass {
static myStaticProp = 42;
constructor() {
console.log(MyClass.myStaticProp); // 42
}
}
4.实例属性
实例属性除了定义在constructor()
方法里面的this
上面,也可以定义在类的最顶层。
class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}
上面代码中,实例属性this._count
定义在constructor()
方法里面。另一种写法是,这个属性也可以定义在类的最顶层,其他都不变。
class IncreasingCounter {
_count = 0;
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}
5.私有属性和私有方法
私有方法和私有属性,是只能在类的内部访问的方法和属性,外部不能访问。
class
加了私有属性。方法是在属性名之前,使用#
表示。
class IncreasingCounter {
#count = 0;
get value() {
console.log('Getting the current value!');
return this.#count;
}
increment() {
this.#count++;
}
}
上面代码中,#count
就是私有属性,只能在类的内部使用(this.#count
)。如果在类的外部使用,就会报错。
这种写法不仅可以写私有属性,还可以用来写私有方法。
class Foo {
#a;
#b;
constructor(a, b) {
this.#a = a;
this.#b = b;
}
#sum() {
return #a + #b;
}
printSum() {
console.log(this.#sum());
}
}
上面代码中,#sum()
就是一个私有方法。
总结一下
类的所有方法都定义在类的
prototype
属性上面。静态方法(类的方法。加static)
静态属性(类的属性,加static)
实例属性