类构造函数
方法名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);
|
默认情况下,类构造函数会在执行后返回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); console.log(p2 instanceof Person)
|
类构造函数与普通构造函数区别
调用类构造函数必须使用new操作符,而普通构造函数如果不使用new调用,那么就会以全局的this作为内部对象,调用类构造函数没有使用new则会抛出错误
类构造函数在实例化后,可以在实例上引用它
1 2 3 4
| class Person{} let p1=new Person();
let p2=new p1.constructor();
|
把类当成特殊的函数
1 2 3
| class Person{} console.log(Person); console.log(typeof Person)
|
类标识符有prototype属性,而这个原型也有一个constructor属性指向类自身
1 2 3 4
| class Person{
} console.log(Person===Person.prototype.constructor);
|
使用instanceof检查一个对象和类构造函数,以确定对象是不是类的实例。
1 2
| let p=new Person(); console.log(p instanceof Person)
|
类本身具有和普通构造函数一样的行为,在类的上下文中,类本身使用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); console.log(p1.constructor=== Person); console.log(p1 instanceof Person.constructor) let p2=new Person.constructor(); console.log(p2 instanceof Person) console.log(p2 .constructor=== Person); console.log(p2 instanceof Person.constructor)
|
实例,原型和类成员
实例成员
每次通过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) console.log(p1.sayAge(18)===p2.sayAge(19))
|
原型方法
为了在实例间共享方法,类定义语法在类块中定义的方法作为原型方法
1 2
| console.log(p1.sayAge===Person.prototype.sayAge);
|
可以把方法定义在类构造函数或者类块中,但不能在类块给原型添加原始值或对象作为成员数据,但是可以在类块外部手动添加成员数据
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);
|
静态类方法
这些方法通常用于执行不特定于实例的操作,也不要求存在类的实例
与原型成员类似,静态成员每个类上只能有一个
静态类成员在类定义中使用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())
|
关于继承
[]: 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()
|
继承内置类型
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)); [this[i],this[j]]=[this[j],this[i]] } } } let a=new SuperArray(1,2,3,4,5); a.shuffle() console.log(a);
|