0%

类构造函数

方法名constructor会告诉解释器在使用new操作符创建类的新实例时,应该调用这个函数,构造函数的定义不是必须的,不定义构造函数相当于将构造函数定义为空函数

使用new调用类的构造函数会执行如下操作:

  • 在内存中创建一个新对象
  • 这个新对象内部的[[Prototype]]即__proto__指针被赋值为构造函数的prototype属性
  • 构造函数内部的this被赋值为这个新对象(即this指向新对象)
  • 执行构造函数内部的代码(给新对象添加属性)
  • 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象
1
2
3
4
5
class Person{

}
let p1=new Person();
console.log(p1.__proto__===Person.prototype);//true

默认情况下,类构造函数会在执行后返回this对象,构造函数返回的对象会被用作实例化的对象,如果没用引用新创建的this对象,那么这个对象会被销毁。不过,如果,返回的不是this对象,而是其他对象,那么这个对象不会通过instanceof操作符检测出跟类有关联。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person{
constructor(override){
this.foo="foo";
if(override){
return {
bar:"bar"
}
}
}
}
let p1=new Person(),p2=new Person(true);
console.log(p1)
console.log(p1 instanceof Person);
console.log(p2);//没有引用构造函数创建的this对象,对象被销毁而返回的其他对象与类没有关系
console.log(p2 instanceof Person)

类构造函数与普通构造函数区别

调用类构造函数必须使用new操作符,而普通构造函数如果不使用new调用,那么就会以全局的this作为内部对象,调用类构造函数没有使用new则会抛出错误

1
let p3=Person();//TypeError

类构造函数在实例化后,可以在实例上引用它

1
2
3
4
class Person{}
let p1=new Person();
//使用对类构造函数的引用创建一个新实例
let p2=new p1.constructor();

把类当成特殊的函数

1
2
3
class Person{}
console.log(Person);//class Person{}
console.log(typeof Person)//function

类标识符有prototype属性,而这个原型也有一个constructor属性指向类自身

1
2
3
4
class Person{

}
console.log(Person===Person.prototype.constructor);//true

使用instanceof检查一个对象和类构造函数,以确定对象是不是类的实例。

1
2
let p=new Person();
console.log(p instanceof Person)//true

类本身具有和普通构造函数一样的行为,在类的上下文中,类本身使用new调用时就被当做构造函数,类中的constructor方法不会被当做构造函数,

1
2
3
4
5
6
7
8
9
10
11
12
class Person{

}
let p1=new Person();
console.log(p1 instanceof Person);//true
console.log(p1.constructor=== Person);//true
console.log(p1 instanceof Person.constructor)//false
let p2=new Person.constructor();
console.log(p2 instanceof Person)//false
console.log(p2 .constructor=== Person);//false
console.log(p2 instanceof Person.constructor)//true

实例,原型和类成员

实例成员

每次通过new调用类标识符时都会执行类构造函数,可以为新创建的实例this添加自有属性。每个实例都对应一个唯一的成员对象,所有成员都不会在原型上共享

1
2
3
4
5
6
7
8
9
10
11
12
class Person{
constructor(){
this.name=new String('Jack');
this.sayName=()=>console.log(this.name);
}
sayAge(age){
console.log(age)
}
}
let p1=new Person(),p2=new Person();
console.log(p1.name===p2.name)//false
console.log(p1.sayAge(18)===p2.sayAge(19))//true

原型方法

为了在实例间共享方法,类定义语法在类块中定义的方法作为原型方法

1
2
 //在类块中定义的所有内容都会定义在类的原型上
console.log(p1.sayAge===Person.prototype.sayAge);//true

可以把方法定义在类构造函数或者类块中,但不能在类块给原型添加原始值或对象作为成员数据,但是可以在类块外部手动添加成员数据

1
Person.greeting="My name is"

类方法等同于对象属性因此可以使用字符串,符号或计算的值作为键,

也支持获取和设置访问器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const symbolKey=Symbol('symbolkey');
class Person(){
stringKey(){
console.log('invoked stringKey')
}
[symbolKey](){
console.log('invoked symbolKey')
}
['computed'+'Key'](){
console.log('invoked computedKey')
}
set name(newvalue){
this.name_=newName;
}
get name(){
return this.name_
}

}
let p=new Person()
p.stringKey();
p[symbolKey]();
p.computedKey();
p.name='jake';
console.log(p.name);//Jake

静态类方法

这些方法通常用于执行不特定于实例的操作,也不要求存在类的实例

与原型成员类似,静态成员每个类上只能有一个

静态类成员在类定义中使用static关键字作为前缀,在静态成员中,this引用类自身,其他所有约定跟原型成员一样,静态类方法适合作为实例工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person{
constructor(){
this.name=new String('Jack');
this.sayName=()=>console.log(this.name);
}
sayAge(age){
console.log(age)
}
static create(){
return new Person(Math.floor(Math.random()*100))
}
}
console.log(Person.create())//Person {name: String, sayName: ƒ}name: String {'Jack'}sayName: ()=>console.log(this.name)[[Prototype]]: Object

关于继承

[]: https://www.bookstack.cn/read/es6-3rd/spilt.1.docs-class-extends.md

关于super

[]: https://www.bookstack.cn/read/es6-3rd/spilt.3.docs-class-extends.md

抽象基类

通过new.target保存通过new关键字调用的类或函数在普通函数中调用new.target返回undefined,通过在实例化时检测new.target是不是抽象基类,可以阻止对抽象基类的实例化

1
2
3
4
5
6
7
8
9
10
11
class Vehicle{
constructor(){
console.log(new.target);
if(new.target===Vehicle){
throw new Error('Vehicle cannot be directly instantiated')
}
}
}
class Bus extends Vehicle{}
new Bus();
new Vehicle()//testIframe.html:193 Uncaught Error: Vehicle cannot be directly instantiated

继承内置类型

1
2
3
4
5
6
7
8
9
10
11
class SuperArray extends Array{
shuffle(){
for(let i=this.length-1;i>=0;i--){
let j=Math.floor(Math.random(0,i+1));//0-i+1向下取整
[this[i],this[j]]=[this[j],this[i]]
}
}
}
let a=new SuperArray(1,2,3,4,5);
a.shuffle()
console.log(a);