老生常谈ES6中的类

大多数面向对象的编程语言都支持类和类继承的特性,而JS却不支持这些特性,只能通过其他方法定义并关联多个相似的对象,这种状态一直延续到了ES5。由于类似的库层出不穷,最终还是在ECMAScript 6中引入了类的特性。本文将详细介绍ES6中的类

ES5近似结构

在ES5中没有类的概念,最相近的思路是创建一个自定义类型:首先创建一个构造函数,然后定义另一个方法并赋值给构造函数的原型

function PersonType(name) { this.name = name; } PersonType.prototype.sayName = function() { console.log(this.name); }; let person = new PersonType("huochai"); person.sayName(); // 输出 "huochai" console.log(person instanceof PersonType); // true console.log(person instanceof Object); // true

这段代码中的personType是一个构造函数,其执行后创建一个名为name的属性给personType的原型添加一个sayName()方法,所以PersonType对象的所有实例都将共享这个方法。然后使用new操作符创建一个personType的实例person,并最终证实了person对象确实是personType的实例,且由于存在原型继承的特性,因而它也是object的实例

许多模拟类的JS库都是基于这个模式进行开发,而且ES6中的类也借鉴了类似的方法

类的声明

ES6有一种与其他语言中类似的类特性:类声明。同时,它也是ES6中最简单的类形式

【基本的类声明语法】

要声明一个类,首先编写class关键字,紧跟着的是类的名字,其他部分的语法类似于对象字面量方法的简写形式,但不需要在类的各元素之间使用逗号分隔

class PersonClass { // 等价于 PersonType 构造器 constructor(name) { this.name = name; } // 等价于 PersonType.prototype.sayName sayName() { console.log(this.name); } } let person = new PersonClass("huochai"); person.sayName(); // 输出 "huochai" console.log(person instanceof PersonClass); // true console.log(person instanceof Object); // true console.log(typeof PersonClass); // "function" console.log(typeof PersonClass.prototype.sayName); // "function"

通过类声明语法定义PersonClass的行为与之前创建PersonType构造函数的过程相似,只是这里直接在类中通过特殊的constructor方法名来定义构造函数,且由于这种类使用简洁语法来定义方法,因而不需要添加function关键字。除constructor外没有其他保留的方法名,所以可以尽情添加方法

私有属性是实例中的属性,不会出现在原型上,且只能在类的构造函数或方法中创建,此例中的name就是一个私有属性。建议在构造函数中创建所有私有属性,从而只通过一处就可以控制类中的所有私有属性

类声明仅仅是基于已有自定义类型声明的语法糖。typeofPersonClass最终返回的结果是"function",所以PersonClass声明实际上创建了一个具有构造函数方法行为的函数。此示例中的sayName()方法实际上是PersonClass.prototype上的一个方法;与之类似的是,在之前的示例中,sayName()也是personType.prototype上的一个方法。通过语法糖包装以后,类就可以代替自定义类型的功能,不必担心使用的是哪种方法,只需关注如何定义正确的类

[注意]与函数不同的是,类属性不可被赋予新值,在之前的示例中,PersonClass.prototype就是这样一个只可读的类属性

【为何使用类语法】

尽管类与自定义类型之间有诸多相似之处,但是它们之间仍然有一些差异

1、函数声明可以被提升,而类声明与let声明类似,不能被提升真正执行声明语句之前,它们会一直存在于临时死区中

2、类声明中的所有代码将自动运行在严格模式下,而且无法强行让代码脱离严格模式执行

3、在自定义类型中,需要通过Object.defineProperty()方法手工指定某个方法为不可枚举;而在类中,所有方法都是不可枚举的

4、每个类都有一个名为[[Construct]]的内部方法,通过关键字new调用那些不含[[Construct]]的方法会导致程序抛出错误

5、使用除关键字new以外的方式调用类的构造函数会导致程序抛出错误

6、在类中修改类名会导致程序报错

了解了这些差异之后,可以用除了类之外的语法为之前示例中的PersonClass声明编写等价代码

// 直接等价于 PersonClass let PersonType2 = (function() { "use strict"; const PersonType2 = function(name) { // 确认函数被调用时使用了 new if (typeof new.target === "undefined") { throw new Error("Constructor must be called with new."); } this.name = name; } Object.defineProperty(PersonType2.prototype, "sayName", { value: function() { // 确认函数被调用时没有使用 new if (typeof new.target !== "undefined") { throw new Error("Method cannot be called with new."); } console.log(this.name); }, enumerable: false, writable: true, configurable: true }); return PersonType2; }());

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wyjypx.html