目录
Class
ES6 的类,完全可以看作构造函数的另一种写法。类的数据类型就是函数,类本身就指向构造函数。使用的时候,也是直接对类使用new
命令,跟构造函数的用法完全一致。注意直接运行会报错。
class Point {
// ...
}
typeof Point // "function"
Point.prototype.constructor === Point // true
类中定义的方法注意点
1.类的所有方法都定义在类的prototype
属性上面,所以我们可以通过Object.assign()
方法可以很方便地一次向类添加多个方法。例如下面:
class Point {
constructor(){
// ...
}
}
Object.assign(Point.prototype, {
toString(){},
toValue(){}
});
2.类的内部所有定义的方法,都是不可枚举的(non-enumerable)。
其它注意点
写法
类中的方法前面不需要加上function
这个关键字,直接把函数定义放进去了就可以了。另外,方法与方法之间不需要逗号分隔,加了会报错。
严格模式
类和模块的内部,默认就是严格模式,所以不需要使用use strict
指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。
不存在提升
类中不存在变量提升(hoist)。
constructor()
方法
constructor()
方法是类的默认方法,通过new
命令生成对象实例时,自动调用该方法。一个类必须有constructor()
方法,如果没有显式定义,一个空的constructor()
方法会被默认添加。
注意constructor()
方法默认返回实例对象(即this
),可以指定返回另外一个对象。 例如下面:
class Foo {
constructor() {
return Object.create(null);
}
}
new Foo() instanceof Foo
// false
取值函数(getter)和存值函数(setter)
在“类”的内部可以使用get
和set
关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。
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'
静态方法
在一个方法前,加上static
关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class Foo {
static classMethod() {
return 'hello';
}
}
Foo.classMethod() // 'hello'
var foo = new Foo();
foo.classMethod()
// TypeError: foo.classMethod is not a function
注意,如果静态方法包含this
关键字,这个this
指的是类,而不是实例。
父类的静态方法,可以被子类继承。例如下面:父类Foo
有一个静态方法,子类Bar
可以调用这个方法。静态方法也是可以从super
对象上调用的。
class Foo {
static classMethod() {
return 'hello';
}
}
class Bar extends Foo {
}
Bar.classMethod() // 'hello'
静态属性
在实例属性的前面,加上static
关键字。
class Foo {
static prop = 1;
}
Foo.prop//1
默认属性
写在constructor函数外定义的属性值,会先执行默认属性值,然后在constructor函数中再对属性进行修改。
class Person {
//设置默认属性
age = 14;
name = 'zhangsan';
sex = 'male';
//在这里可以对默认属性进行修改
constructor(name,sex) {
this.name=name;
this.age = 18;
this.sex = sex;
}
}
const p = new Person('Alex');
console.log(p.name); //Alex
console.log(p.age); //18
console.log(p.sex); //undefined
私有属性
在属性名之前,使用#
表示
例如下面:#count
就是私有属性,只能在类的内部使用(this.#count
)。如果在类的外部使用,就会报错。
class IncreasingCounter {
#count = 0;
get value() {
console.log('Getting the current value!');
return this.#count;
}
increment() {
this.#count++;
}
}
in
运算符判断私有属性
in
运算符可以用来判断私有属性。只能用在定义该私有属性的类的内部。
class A {
#foo = 0;
m() {
console.log(#foo in this);
}
}
A.prototype.m()//false
let a = new A()
a.m()//true
子类从父类继承的私有属性,也可以使用in
运算符来判断。
例如下面:SubA
从父类继承了私有属性#foo
,in
运算符也有效。
class A {
#foo = 0;
static test(obj) {
console.log(#foo in obj);
}
}
class SubA extends A {};
A.test(new SubA()) // true
其它相关
属性表达式
类的属性名,可以采用表达式。
let methodName = 'getArea';
class Square {
constructor(length) {
// ...
}
[methodName]() {
// ...
}
}
Class表达式
与函数一样,类也可以使用表达式的形式定义。例如下面:
这个类的名字是Me
,但是Me
只在 Class 的内部可用,指代当前类。在 Class 外部,这个类只能用MyClass
引用。
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
name 属性
返回紧跟在class
关键字后面的类名。注意Class表达式中class后跟的是类名,前面的是引用。
class Point {}
Point.name // "Point"
Generator 方法
如果某个方法之前加上星号(*
),就表示该方法是一个 Generator 函数。
例如下面:Foo
类的Symbol.iterator
方法前有一个星号,表示该方法是一个 Generator 函数。Symbol.iterator
方法返回一个Foo
类的默认遍历器,for...of
循环会自动调用这个遍历器。
class Foo {
constructor(...args) {
this.args = args;
}
* [Symbol.iterator]() {
for (let arg of this.args) {
yield arg;
}
}
}
for (let x of new Foo('hello', 'world')) {
console.log(x);
}
// hello
// world
new.target属性
Class 内部调用new.target
,返回当前 Class。如下:
class Rectangle {
constructor(length, width) {
console.log(new.target === Rectangle);
this.length = length;
this.width = width;
}
}
var obj = new Rectangle(3, 4); // 输出 true
子类继承父类时,new.target
会返回子类。如下:
class Rectangle {
constructor(length, width) {
console.log(new.target === Rectangle);
// ...
}
}
class Square extends Rectangle {
constructor(length, width) {
super(length, width);
}
}
var obj = new Square(3); // 输出 false