20、类

最后更新于:2022-04-02 06:07:36

  ES6正式将类(Class)的概念在语法层面标准化,今后不必再用构造函数模拟类的行为。而ES6引入的类本质上只是个语法糖(即代码更为简洁、语义更为清晰),其大部分功能(例如继承、封装和复用等)均可在ES5中实现,只不过现在能用更符合面向对象的语法来操作类。但诸如接口、protected修饰符等一些面向对象常用的语法,ES6没有给出相关标准。 ## 一、创建   在ES5时代,可以像下面这样模拟一个类,先声明一个构造函数,然后在其原型上定义共享的方法,最后与new运算符组合实例化一个类。 ~~~ function People(name) { this.name = name; } People.prototype.getName = function () { return this.name; }; var people = new People("strick"); people.getName(); //"strick" ~~~   本节接下来的内容会与这个示例有一些关联。 **1)类声明**   类的创建方式与函数类似,也有两种:类声明和类表达式。类声明必须包含名称和class关键字,下面也创建一个People类,其主体由一对花括号包裹,它的自有属性和方法都与前一个People类相同。注意,每个类有且只有一个构造函数:constructor(),如果没有显式的声明,那么会有一个空的构造函数以供调用。 ~~~ class People { constructor(name) { this.name = name; } getName() { return this.name; } } var people = new People("strick"); people.getName();           //"strick" typeof People;             //"function" typeof People.prototype.getName;  //"function" ~~~   在代码的最后,调用了两次typeof运算符,由于此处的People类相当于上一个示例模拟的People类,只不过写法不同,因此两次运算的计算结果都是“function”,这也从侧面再次印证ES6的类仅仅是个语法糖。   虽然两种类非常相似,但是ES6中的类有其独有的特性,具体如下所列:   (1)类声明和即将要讲解的类表达式都不会被提升。   (2)类中的代码在执行时,会强制开启严格模式。   (3)类的所有方法都不可枚举,并且不能与new组合使用。 **2)类表达式**   在类表达式中,名称是可选的,但class关键字依然是必需的。如果包含名称,那么叫做命名类表达式,反之,叫做匿名类表达式,如下所示。 ~~~ var People = class { //匿名类表达式 }; var People = class Man { //命名类表达式 }; ~~~   命名类表达式中的名称只能在类的内部访问,如果在外部贸然使用,那么就会抛出未定义的错误。下面的例子演示了名称的特点和局限。 ~~~ var People = class Man { getSelf() { typeof Man; //"function" Man.name; //"Man" new Man().getAge(); //28 } getAge() { return 28; } }; var people = new People(); people.getSelf(); People.name; //"Man" Man.name; //Man未定义的错误 ~~~   在getSelf()方法中先将typeof运算符应用于Man,然后访问Man的name属性,最后调用其实例的getAge()方法。在命名类的外部分别访问People和Man的name属性,前者能得到预期的结果,而后者却会抛出错误。   与函数表达式类似,类表达式也能立即执行,只是要像下面这样,先在class关键字之前加new,然后在类的主体后面跟一对圆括号,里面的参数会传递给构造函数。 ~~~ var people = new class { constructor(name) { this.name = name; } getName() { return this.name; } }("strick"); people.getName(); //"strick" ~~~ ## 二、成员   类的成员既可以是普通的原型方法或自有属性,还可以是有特殊功能的构造函数、生成器、静态方法和访问器属性等,并且成员名可以是表达式。 **1)自有属性**   类中的自有属性可以作为this对象的属性,并且一般都会在构造函数中执行初始化,如下所示。 ~~~ class People { constructor() { this.name = "strick"; } } ~~~ **2)访问器属性**   在类中的访问器属性,其存取语法和ES5对象字面量中的相同,也需要get和set两个关键字,具体实现如下所示。 ~~~ class People { get prop() { return `getter:${this.name}`; } set prop(value) { this.name = value; } } var people = new People(); people.prop = "strick"; console.log(people.prop); //"getter:strick" ~~~   访问器属性还有一个便捷的地方,就是它和原型方法一样,也能被子类继承。 **3)计算成员名**   类中的成员名既可以是标识符,也可以是要计算的表达式(如下代码所示),其声明语法和ES5对象字面量中的相同,也需要用一对方括号包裹。 ~~~ var method = "getAge"; class People { ["get" + "Name"]() { return "strick"; } [method]() { return 28; } } var people = new People(); people.getName();   //"strick" people.getAge(); //28 ~~~ **4)生成器**   只要在某个方法之前加上星号(\*),那么这个方法就能变为生成器,注意观察下面代码中的getName()方法。关于生成器的具体用法可以参考[第19篇](https://www.cnblogs.com/strick/p/10344953.html)。 ~~~ class People { *getName() { yield "strick"; } } var people = new People(), iterator = people.getName(); iterator.next(); //{value: "strick", done: false} ~~~   如果方法的名称是内置符号Symbol.iterator并且是一个生成器方法,那么就成功的为类创建了一个默认的迭代器,这也意味着类的实例能被for-of循环,具体实现可参考下面的代码。 ~~~ class People { *[Symbol.iterator]() { for (const item of [1, 2]) { yield item; } } } var people = new People(); /******************** 1 2 ********************/ for(var value of people) { console.log(value); } ~~~ **5)静态方法**   ES6新增了static关键字,可把类中的方法(除了构造函数)定义成静态的。要调用静态方法只能通过类本身,而不是实例化的类,如下代码所示。除了方法之外,static关键字还适用于访问器属性。 ~~~ class People { static getName() { return "strick"; } } People.getName(); //"strick" ~~~   虽然ES6明确提出了静态方法,但是没有将静态属性一并标准化。如果要使用静态属性,可以像下面这样用变通的方式定义。 ~~~ People.age = 28; ~~~ ***** > 原文出处: [博客园-ES6躬行记](https://www.cnblogs.com/strick/category/1372951.html) [知乎专栏-ES6躬行记](https://zhuanlan.zhihu.com/pwes6) 已建立一个微信前端交流群,如要进群,请先加微信号freedom20180706或扫描下面的二维码,请求中需注明“看云加群”,在通过请求后就会把你拉进来。还搜集整理了一套[面试资料](https://github.com/pwstrick/daily),欢迎浏览。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200)
';