《JavaScript》ES6/ES7-class详解(二)

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静态方法;

猜你喜欢

转载自blog.csdn.net/zy21131437/article/details/106730451