11. 类对象
类对象
class用法跟let和const一样,不存在变量提升,也不能重复声明类名,JS中的类(class)是在ES6中被推出为关键字,实际上也是通过原型构成的模拟类
ES5面对对象写法和传统的面向对象语言(如C++和JAVA)差异很大,很容易让新学习这门语言的人感到困惑。所以在ES6中提供了更接近传统语言的写法,引入了class这个概念,作为对象的模板,通过class关键字,可以定义类。
ES6中的class可以看作只是一个语法糖,它的大部分功能ES5都可以做到,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程的语法而已
11.1 用法
//ES5写法
function Person(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
}
Person.prototype.sayName=function(){
alert("hello"+this.name);
}
//ES6写法
class Person{
constructor(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
}
sayName(){
alert("hello"+this.name);
}//其实就是ES6中函数的简写,但是必须用这种简写形式创建方法
}
注意:
我们通常只会在原型中放方法,在ES5的语法中虽然在原型中可以放普通对象,但是其实不推荐这样做,所以在用class声明的类中只允许在constructor()函数中放入对象,而在constructor()函数外只能放方法
构造函数的prototype属性在ES6的class中依然存在,事实上,class中定义的所有方法全部都作为prototype属性的方法保存
class Fun{ construct(){ } say(){ } add(){ } } //等同于 function Fun(){ construct(){ }, say(){ }, add(){ } }
如果不传入参数,constructor()函数可以不用写,但是在解析的时候会自动为这个类加上该函数方法
11.2 静态方法
类相对于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,只能通过类本身来调用,这就是静态方法
**注意:**ES6中规定class内部只有静态方法,并没有静态属性,也就是不能设置静态属性,如果要设置静态属性只能在外面手动设置
class Person{
constructor(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
}
static say(){
alert("hello");
}
}
Person.say();//"hello"
Person.age=18;
console.log(Person.age);//18
11.3 继承
在class中通过extends关键字进行类的继承
class Person{
constructor(name,age,gender){
this.name=name;
this.age=age;
this.gender=gender;
}
sayName(){
alert("hello"+this.name);
}
}
class Studnet extends Person{
constructor(name,age,gender,score){
super(name,age,gender)//必须调用super()方法,不然新建对象时会报错
this.score=score;
}
}
let student=new Student("孙悟空",18,"男",100);
student.sayName();
注意:
**子类必须在constructor()方法中调用super()方法,否则新建实例时会报错。**这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工。如果不调用super()方法,子类就不能得到this对象
子类会将父类的静态方法与静态属性也一起继承,通过子类同样也能用父类的静态方法
11.4 super
super关键字既可以当作函数使用,又可以直接当作对象调用,这两种情况下的super的用法完全不同
**super作为函数使用时,代表调用父类的构造函数。**并且在子类的构造函数中必须执行一次super()方法 注意:
- super当作函数使用时只能用在子类构造函数中,如果用在其它地方就会报错
- 父类可以不写constructor()方法,因为在调用的时候会自动加上该函数,但是子类必须写该方法,因为super()方法必须写在constructor()方法中,而super()方法在继承中又是必写的,不写子类就不具备this,从而无法返回对象出来
class A{
constructor(){
}
}
class B extends A{
constructor(){
super();
}
}
/*
子类B的构造函数之中的super()虽然代表的是父类A的构造函数,但是返回的确实子类B的实例,即super()内部的
this是指向B,所以super()相当于A.prototype.constructor.call(this)
*/
super当作对象使用时,在普通实例方法中指向父类的原型对象,在静态方法中指向父类
class A{ constructor(){ say(){ return "hello"; } } } class B extends A{ constructor(){ super(); console.log(super.say());//"hello" } } /* 在创建实例对象时就会在控制台打印"hello",子类B中的super.say()就是将super当作一个对象进行使用,此时的super在普通实例方法中指向的是A.prototype,super.say()就相当于A.prototype.say() */
由于this是指向子类的,所以如果通过super对某个属性进行赋值,这时的super就是this,赋值的属性就会变成子类实例的属性
class A{ constructor(){ this.x=1; } } class B extends A{ constructor(){ super(); console.log(this.x);//1 this.x=2; console.log(this.x);//2 super.x=3; console.log(super.x)//undefined,由于此时的super的指向是A.prototype console.log(this.x)//3 } } /* super.x=3等同于this.x=3,而读取super.x的时候此时super的指向是A.prototype,也就是 A.prototype.x,因为没有设置实例属性,所以此时的值为undefined */