前言
之前写过一篇剪短的class
文章,这次在学习TS
的时候,发现对于class
的掌握还不是很熟练,于是参考阮一峰老师的《ES6入门》深入学习
写法
如何定义一个类呢?
class Human{
constructor(name, gender, age){
this.name = name
this.gender = gender
this.age = age
}
eat(){
console.log(`${this.name}饿了,吃饭中···`)
}
}
-
constructor 方法
类似于强类型语言的构造方法
constructor
方法是类的默认方法,通过new
操作符生成对象实例时,会自动调用该方法。一个类必须有constructor
方法,如果在定义类时没有显示定义,会被默认添加一个空的constructor
方法。
constructor
方法默认返回实例对象(即this
),但也可以指定返回另一个对象。
class Fn{
constructor (){
return Object.create(null),
}
}
new Fn() instanceof Fn //false
类与ES5
的构造函数主要区别是:
类必须使用new
操作符来调用,而ES5
的构造函数不用new
也会执行。
类的实例
与ES5
一样,实例的属性除非显示定义在本身上,否则都是定义在原型上。
类的所有实例都共享一个原型对象
class Fn{
constructor(x, y){
this.x = x
this.y = y
this.fn = () => {console.log(`这是实例上的方法`)}
}
fn(){
console.log(`这是原型上的方法`)
}
}
const a = new Fn(1, 2)
a.fn() //这是实例上的方法
a.__proto__.fn() //这是原型上的方法
const b = new Fn('a', 'b')
a.__proto__ === b.__proto__ //true
注意点
严格模式
类和模块的内部,默认就是严格模式,所以不需要使用use strict
指定运行模式。只要你的代码写在类或模块中,就只有严格模式可用。不存在变量提升
类不存在变量提升,这与ES5
完全不同
new Fn() //error
class Fn(){}
name属性
本质上,ES6
的类只是ES5
的构造函数的一层包装,所以函数的许多特性都被class
继承,包括name
属性。this的指向
类的方法内部如果含有this
,它默认指向类的实例。但如果单独使用该方法,很可能报错,因为当你调用一个函数时,this
的指向由调用者决定。
静态方法
类相当于实例的原型,所有定义在类中的方法,都会被实例继承,如果在一个方法前加上static
关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就是“静态方法”,并且静态方法还可以和非静态方法重名。
如果静态方法内部包含this
关键字,这个this
指向的是类,不是实例。
class Fn{
constructor(){}
static classMethod(){
this.bar()
}
static bar(){
console.log('我是类方法')
}
bar(){
console.log('我是原型上的方法')
}
}
Fn.classMethod() //我是类方法
父类的静态方法可以被子类继承
实例属性的新写法
实例属性除了定义在constructor()
方法里面的this
上面,也可以定义在类的最顶层。
class Human{
name
age
gender
constructor(){}
}
const a = new Human()
a.name //undefined
a.age //undefined
静态属性
静态属性是类本身的属性,不是定义在实例对象上的属性,目前最新版的Chrome浏览器已经支持类的静态属性了。
class Fn{
static prop = 'Hello World'
constructor(){}
}
Fn.prop //Hello World
new.target属性
new
是从构造函数生成实例对象的命令,ES6为new
命令引入了一个new.target
属性,一般用于构造函数中,返回new
操作符作用对象的构造函数,如果构造函数不是通过nwe
调用的,那么new.target
会返回 undefined
,所以这个属性可以确定构造函数是否通过new
调用。
function fn(value){
if(new.target !== undefined){
this.value= value
}else{
throw new Error('必须使用new操作符生成实例对象')
}
}
const a = new fn(666) //正确
const b = fn(666) //error
在Class内部调用new.target
,返回当前Class,子类继承父类是,new.target
返回子类。
Class的继承
Class可以通过extends
关键字实现继承,相对于ES5通过修改原型链实现继承更加清晰和方便。
class Human{
constructor(name, age, gender){
this.name = name
this.age = age
this.gender = gender
}
eat(){
console.log('吃饭中···')
}
}
class Man extends Human{
constructor(name, age, stature){
super(name, age)
this.stature = stature
this.gender = 'man'
}
}
const zink = new Man('zink', 21, 174)
zink.eat() //吃饭中···
ES5的继承是先创造子类对象this
,然后将父类的方法绑定到this
上(Parent.apply(this)
)。ES6的机制跟ES5完全不同,先将父类实例对象的属性和方法添加到this
上,再用子类的构造哈数修改this
。
注意点:
- 子类必须在构造方法里调用
super
方法,且必须在调用super
方法之后在修改this
的属性,否则会报错。
super关键字
super
这个关键字,既可以当做函数使用,也可以当做对象使用。但是用法不同。
作为函数调用
当super
作为函数调用时,只能在子类的constructor方法
里调用,代表父类的构造函数。super
虽然代表了父类的构造函数,但是返回的是子类的实例,也就是说super
内部的this
指向子类的实例,所以super()
相当于是父类.prototype.constructor.call(this)
作为对象调用
在普通方法中,super
表示父类的原型对象,此时只能访问父类原型实例上的属性,并且通过super
访问的方法内部的this
指向子类实例。
在静态方法中,super
表示指向父类,此时只能访问父类的静态属性和方法(即用static
关键字标注的属性和方法),此时方法内部的this
指向子类而不是子类实例