class简介
类实际上是一个“特殊的函数”,就像能够定义函数表达式和函数声明一样,另外class都必须遵循严格模式,类的语法有两个组成部分:
- 类声明
- 类表达式
类声明:
通过使用带有关键字class的类名(下例的类名就是ES6)来声明一个类后面加一对大括号
class ES6{
}
另外注意,类的定义不会提升,这个跟函数声明不一样,函数声明不管在哪定义,都可以在任意地方调用,而类的调用必须在类声明之后,否则会抛出一个ReferenceError异常。
类表达式:
类表达式是定义类的另一种方式,类表达式可以是被命名的表达式,也可以是匿名的。赋予一个命名类表达式的名称是类主体的本地名称
//匿名类
let a=class {
constructor(a,b){
this.a=a;
this.b=b
}
}
//命名类,这边的命名最好和主体的名字一致
let a=class a{
constructor(a,b){
this.a=a;
this.b=b
}
}
注意,类表达式也同样有声明提升的问题,调用必须在声明之后
constructor方法:是一个特殊的方法,一个class类只有一个constructor方法,这个方法用于创建和初始化
实例上
//ES5以及之前
function ES5(a,b) {
this.a=a;
this.b=b
}
ES5.prototype.func=function(){
return this.a+this.b
}
let result=new ES5(1,2)
在ES6之前,都是通过构造函数实现的,到了ES6引入了类的概念,把上面的修改转到类的写法
//ES6
class ES6{
constructor(a,b){
this.a=a;
this.b=b
}
func(){
return this.a+this.b
}
}
let result=new ES5(1,2)
可以看出,constructor的作用就是构造函数的初始化,而原型上的方法也被写到了类里面(注意类的里面不需要写function),其他基本一致,从这看出,ES6的写法只是将对象原型的写法更在清晰,更像是面向对象编程的语法而已。
class特性
那么class的写法和原型对象写法有什么共性和区别呢?
实例化
都是通过new关键字将对象实例化,例如
let a=new ES5();
let b=new ES6();
但是,原型对象可以不通过new来直接引用函数使用,而class而不行,会报错
let a=ES5(); //直接调用
let b=ES6(); //会直接报错
等同于构造函数,通过下面的例子可以看出,class类和原型对象,其实基本就是一致的
class g{
constructor(){
this.ga = "a"
}
newTest(){
console.log(this.ga
);
}}
console.log(typeof g); //function
console.log(g===g.prototype.constructor); //true
let h=new g();
h.newTest(); //'a'
console.log(h.constructor === g.prototype.constructor); //true
给类添加方法
Obeject.assign(target,a,b....n)
这个方法在对象中学过了,是将对象中的属性添加到目标对象中,如果属性已存在,会被覆盖
class es1{}
Object.assign(es1.prototype,{
newTest(){},
newtest2(){}
})
class的属性都是不可枚举的,ES5中的构造函数则都可以枚举
class es1{
newTest(){};
newtest2(){};
}
Object.assign(es1.prototype,{
newTest3(){},
newtest4(){}
})
console.log(Object.keys(es1.prototype)); //["newTest3", "newtest4"]
console.log(Object.getOwnPropertyNames(es1.prototype)); //["constructor", "newTest", "newtest2", "newTest3", "newtest4"]
这例子可以看出,class本身的方法都是不可枚举的,只有使用getOwnPropertyNames获取;
另外,class内部定义的外部不能访问
//命名类
let esha=class ha{
getName(){
console.log(ha.name);
}
}
let dd=new esha();
dd.getName(); //ha
//console.log(ha.name); //报错,ha外部不能访问
console.log(esha.name); //可以访问,输出:ha
立即执行,立即执行和内部定义是比较常用的
let hba=new class hb{
constructor(name){
this.name =name
}
newTest(){
console.log(this.name);
}}("yzq")
hba.newTest()
二次声明
在ES5中,如果同一个函数名被多次声明不会报错的,但是在ES6中class的类名如果相同会直接报错;
静态方法
let Es6a=class{
constructor(){};
static test(){
console.log('yzqa');
}
}
静态方法无法被实例化后的实例调用,如果调用会直接报错
let Es6a=class{
constructor(){};
static test(){
console.log('yzqa');
}
}
let Es6a1=new Es6a();
console.log(Es6a1.test()); //报错
另外Static this指向的是类(个人理解就是优先指向同样是静态static的方法,如果方法名重复,都是static则是最后一个)
let Es6a=class{
constructor(){};
static static getName(){return this.test()}
test(){
console.log('yzqa');
}
test(){
console.log("new yzqa");
}
}
Es6a.getName() //yzqa
继承
关键字是extends,通过继承来获得父类的属性和方法,包括静态方法
let Es6b=class{
constructor(){};
static getName(){return 'yzqa';}
static getNewname(){
console.log("why999");
}
}
class Es6b1 extends Es6b{
static getName(){
console.log(super.getName()+"v5");
}
}
Es6b1.getName(); //yzqav5
Es6b.getNewname(); //why999
super关键字,调用父级元素的方法
class中new.target指向的是当前的类,哪怕是继承的子类使用super调用父级的new.target,new.target指向的依旧是当前的子类,如下例
class Es6c{
constructor(){
console.log(new.target===Es6c ? "success" : "fail");
}
}
class Es6c1 extends Es6c{
constructor(){
super();
}
}
let _es6c=new Es6c(); //success
let _es6c_c=new Es6c1(); //fail