class
class,ES6引入的一个关键字,其用法基本上等同于ES5中的原型的继承,所以class关键字的引入并没有为JavaScript添加新的继承模型,而是仅仅作为一个语法糖被使用,因此JS中的class和其他面向对象语言中的class是两码事;
因此,在学习class之前,如果能先了解prototype和继承的运行机制,这样对class的学习将事倍功效;
先看一个简单的对比
//ES5的prototype
function ES5(a,b){
this.a = a;
this.b = b;
}
ES5.prototype = {
func:function(){
return this.a + this.b
}
}
//ES6的
class ES6{
constructor(a,b){
this.a = a;
this.b = b;
}
//es6增强字面量写法
func(){
return this.a + this.b
}
}
如果你了解过构造函数,prototype等,那么相信你很快就可以通过对比,基本了解class的使用,发现其效果与构造函数大同小异,比如在ES5的用法中对参数的初始化等同于在class中的constructor函数的使用,而在prototype上定义的方法等同于在class内直接定义函数
下面正式记录class的用法:
创建
类的创建和函数的创建一样,都是通过关键字创建,函数中使用的是关键字function,类则是通过关键字class,同样,类的声明方式也有两种:类声明和类表达式
类声明
//类声明
class ES6{
constructor(){}
}
其中,类声明和函数声明不同,函数声明会提升,但类声明不会提升,如果在声明前使用类,那么会抛出一个错误;
let p = new Person();//报错
class Person{}
类表达式
类表达式分为:具名类表达式和匿名类表达式,两者的区别在于类的内部有一个默认属性:name,然后其值不同;
//具名类表达式
const ES6 = class ES6_1{
constructor(){}
}
console.log(ES6.name) //ES6_1
//匿名
const ES6 = class{
constructor(){}
}
console.log(ES6.name) //ES6
验证
另外,如果你对类ES6进行类型验证
console.log(typeof ES6) //function
发现,其实这个ES6的类型是function类型,也从侧面反应了其实是ES5构造函数那一套东西,既然是function类型,那么在js中,所有function类型都有一个prototype属性,如果要给prototype扩展方法,如下例,是不是就是给类扩展方法?
class ES6{}
Object.assign(ES6.prototype,{
func1(){},
func2(){}
})
事实确实可以,另外通过调用类ES6上的方法也可以判断ES6的类型,假设现在有这样一个类
class Animal {
constructor() { }
speak() {
console.log(1);
}
}
如果Animal是对象,那么调用Animal.speak(),应该是可以执行的,但经过测试,不可以,调用speak方法必须通过Animal.prototype.speak()
Animal.speak(); //报错
Animal.prototype.speak(); //1
从这个例子也可以看出,class其实就是原型对象的用法,它只是将定义在prototype上的方法,直接写在了class内
constructor方法
在class中的constructor是一个特殊的方法,这种方法用于创建和初始化一个由class创建的对象,一个类只能拥有一个constructor方法;
class ES6{
constructor(a,b){
this.a = a;
this.b = b;
}
}
//等同于
function ES6(a,b){
this.a = a;
this.b = b;
}
extends和super
extend,是一个关键字,作用是为当前的类设置一个父类,设置后,可以从父类那么继承到父类上的方法;
class Animal {
constructor(name) { this.name = name; }
speak() {
console.log(this.name);
}
}
// 创建DOg父类
class Dog extends Animal {
speak() {
console.log(`这是${this.name}`);
}
}
let dog = new Dog("旺财");
console.log(dog.speak()) //旺财
我学到这里的时候,会有个疑问,子类Dog中并没有设置name属性的地方,为什么dog.speak()会输出旺财,经过实验发现,子类中没有定义自己的constructor,因此会直接使用父类中的contructor,给dog添加了一个name属性,具体等了解了super再一起分析
super,也是一个关键字,作用是调用父级对象上的函数,一共有两种用法,如下:
super(arguments) //必须写在contructor中
super.function(arguments)
**
super(arguments),这个用法必须写在子类的contructor中,作用是调用父类的constructor,比如
class Animal {
constructor(name) { this.name = name; }
speak() {
console.log(this.name);
}
}
// 创建DOg父类
class Dog extends Animal {
constructor(name) {
super(name); //会调用父类上的contructor方法,并将name作为其参数
}
speak() {
console.log(`这是${this.name}`);
}
}
如果子类有contructor方法,那么必须在方法内写上super(),参数可以根据实际情况写或不写;另外如果写了super(),则必须在后续内容的前面,否则会报错,例如:
class Animal {
constructor(name) { this.name = name; }
speak() {
console.log(this.name);
}
}
// 创建DOg父类
class Dog extends Animal {
constructor(name) {
this.name = name; //报错,supe()必须写在最前面
super(name);
}
speak() {
console.log(`这是${this.name}`);
}
}
super.function(arguments),就是调用父类上的方法,这种写法没有具体限制,不需要一定写在contructor里面
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this)
console.log(this.name);
}
}
// 创建DOg父类
class Dog extends Animal {
constructor(name) {
super(name); // 调用超类构造函数并传入name参数
}
speak() {
super.speak();
}
}
let dog = new Dog("旺财");
dog.speak() //new的实例化之后会返回一个对象,该对象中的this指向Dog,
为了更好的理解super,看一个经典示例
class Animal {
constructor(age) {
this.age = 10;
}
}
class Dog extends Animal {
constructor() {
super(); // 调用超类构造函数并传入name参数
console.log(this.age); //此时Dog并没有age属性,因此沿着原型链查到父类上面的属性,返回10
this.age = 2; //设置后,此时的Dog上有个age属性,并且设置值为2
console.log(this.age); //这时候Dog上有age了,因此值是2
//根据规范,没有为什么,就是规范,在子类中使用super对某个值进行赋值操作的时候等同与this
//也就是super.age = 18; 等同于 this.age = 18;
super.age = 18;
//而super.age的值,等同与Animal.prototype.age,很明显Animal的原型上没有age属性
//this.age自然就是上面super.age的值
console.log(super.age,this.age); //undefined,18
}
}
let dog = new Dog();
static
static,是一个关键字,这个关键字使用在类内的方法前面,比如
class ES6{
constructor(a,b){
this.a = a;
this.b = b;
}
static sum(){
return this.a + this.b
}
}
使用static定义的方法称为静态方法,静态方法代表如果该类被实例化,那么静态方法不能被实例对象继承,但是如果是类之间的继承则是可以继承到的,比如实例化上例中的ES6方法
const _es6 = new ES6();
_es6.sum() //报错,找不到该方法
如果是通过extends继承,那么static静态方法将可以被子类继承
class Animal {
constructor(name) {
this.name = name;
}
static out(){
return 10
}
speak() {
console.log(this)
}
}
// 创建DOg父类
class Dog extends Animal {
constructor(name) {
super(name); // 调用超类构造函数并传入name参数
}
}
console.log(Dog.out()) //10
通过例子可以确认,static静态方法可以在类之间继承,但是一旦类被实例化,那么实例化对象将无法继承static静态方法;