一、class概念
es5中,生成实例对象是通过构造函数,实例自身属性和继承方法是分开写的,容易让新手困惑。es6中,引入了class概念,通过关键字class创造类,这种写法更清晰、更像面向对象编程。这是一种语法糖,大部分功能都可以由es5模拟。
class写法:
(1)constructor方法,构造函数,接受参数,给实例添加属性与方法,使用new操作符生成实例时,如果没有constructor方法,系统会默认添加一个空方法。
(2)原型上的方法,方法与方法之间不用逗号隔开,相当于写在construcor.prototype上的方法。所有类的内部方法都是不可枚举的(Object.defineProperty定义enumerable为false),这一点与es5中原型方法有区别。
class Super {
constructor(x, y){
this[x] = x,
this[y] = y;
}
compute(...args){
return args.reduce((res,item) => item + res,0)
}
get myName(){
return 'getter'
}
set myName(val){
return 'setter:' + val
}
}
let superInstance = new Super('name', 'age');
console.log(Object.keys(Super.prototype), Object.getOwnPropertyNames(Super.prototype))
// => [], ["constructor", "compute"]
tip:**class不实用new调用会报错**,传统的构造函数不用new操作符也能调用(底层代码会检查this对象是否为构造函数的实例,instanceof)。<br />(3)class原型上也可以写存取值方法,用来劫持实例属性的访问与修改,这是写在原型constructor.prototype上的方法,且是存在属性描述对象上的 prop in descriptor = > true<br />(4)class内部可以直接通过类名访问到该类。
class Super{
getClass(){
return Supber.name
}
}
注意点:
(1)严格模式,es6的class中默认就是严格模式,无需再写字符串声明严格模式(构造函数中已经添加了 ‘use strict’)。
(2)class不存在函数提升,声明前使用会报错,子类必须在父类后声明(class类是一个通过var声明的函数表达式)。
(3)name属性,class也是一种构造函数的包装,name属性总是返回class后的名字。
(4)class中,this默认指向实例,但是单独引用,或者作为其他对象方法调用,this指向会改变。
class Super{
printName(txt){
console.log(this.print(`hello ${txt}`))
}
print(text){
console.log(text)
}
}
let {printName} = new Super();
printName('三点几嘞') // this => undefined
解决方法:可以在构造函数中,给实例添加bind this后的原型对象方法,或者使用箭头函数。
class Super{
constructor(){
this.printName = this.printName.bind(this)
this.printName2 = ()=> this.printName
}
printName(txt){
console.log(this.print(`hello ${txt}`))
}
print(text){
console.log(text)
}
}
<br />(5)通过static添加**静态方法**:放在类上的函数(不是原型上),所以不会被实例继承,静态方法里的this指向类,不是实例。
class Super{
static print(){
this.bite()
}
static bite(){
console.log('wnagwangwang!')
}
}
子类可以继承父类的静态方法,可以直接通过子类调用,也可以通过子类静态方法中super调用
class Super{
static SuperFoo(){
console.log("I'm your papa")
}
}
class Son extends Super {
static bite(){
super.SuperFoo()
}
}
Son.SuperFoo()
Son.bite()
(6)实例属性新写法<br />除了在constructor中写实例属性,还可以在class中最顶层写,= 赋值操作,这种写法,一眼就能看出来是实例的属性。
class Super{
name = 'your papa'
}
let instance = new Super();
console.log(instance.name)
(7)类的静态属性
class Super{
static name = 'GodFather'
}
console.log(Super.name)
(8)new.target在函数中使用,返回new操作符作用于的构造函数。可以写出只能用于继承,不能实例化的类
class forExtending {
consctructor(){
if(new.target === forExtending){
throw Error('本类不能实例化')
}
}
}
子类继承自父类,父类中的new.target指向父类。
class Super{
constructor(){
console.log(new.target === Super) // => false
}
}
class Son extends Super {
constructor(){
super()
}
}
let son = new Son()
二、class继承
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法,super作为函数调用时,代表父类的构造函数),然后再用子类的构造函数修改this。