无论你喜欢与否,ECMScript 6中将会包含类(class)的概念。在JavaScript中,类的概念一直是两级分化的。由于JavaScript与其他语言的不同,有些人喜欢 JavaScript的无类特征;另一方面,也有一些人讨厌JavaScript的无类特征。那些从C++或者Java转到JavaScript的人,需要克服的最大心理障碍就是接受JavaScript的“无类特征”,这或许也是许多人不喜欢JavaScript或者不学习JavaScript的原因之一。
在JavaScript中并没有正式的类定义,也有一些JavaScript书籍和文章讨论过如何在JavaScript中定义类,其实那些只是自定义的构造函数。在JavaScript中,引用类型是最接近类的东西。下面的这些代码,你是不是已经很熟悉:
- function MyCustomType(value) {
- this.property = value;
- }
- MyCustomType.prototype.method = function() {
- return this.property;
- };
许多时候,大家会把上面代码理解成是在声明一个MyCustomType类,事实上,这仅仅是一个名称为MyCustomType的方法,用来创建一个引用类型实例。和其他方法并无不同之处,它只是使该函数当做构造函数使用罢了。
这段代码根本看不出像是在定义类,事实上,构造函数的定义和其原型对象上的方法有很少的联系。对于JavaScript新手来说,这是完全独立的两部分代码,但其实它们有非常紧密的联系,只是和其他语言定义类不一样而已。
更令人困惑的是,一旦人们提到继承,首先想到的都是父类和子类这些概念,而这些概念只有在真正存在“类”的前提下,才会真正明白其含义。同样,在JavaScript中,继承的语法也是混杂冗长的:
- function Animal(name) {
- this.name = name;
- }
- Animal.prototype.sayName = function() {
- console.log(this.name);
- };
- function Dog(name) {
- Animal.call(this, name);
- }
- Dog.prototype = new Animal(null);
- Dog.prototype.bark = function() {
- console.log("Woof!");
- };
实现继承需要两个步骤,创建构造函数和重写原型对象,这样的代码是非常混乱的。
在第一版的《Professional JavaScript for Web Developers》中,我直接使用“class”这个专有名词。在后来我收到的反馈中发现,用户对这个名称是存在困惑和异议的,于是,在第二版里面我把所有的“class”换成了“type”。这样就消除了很多困惑。
然而,这仍然还是个比较突出的问题,自定义类型定义还是令人困惑、语法混乱的。两个类型之间实现继承更加复杂。没有什么简单的方法可以直接调用超类方法。如果你认为这不是个问题,那么请看看下面的这些JavaScript框架,它们已经推出了自己的自定义类型、继承:
- YUI——Y.extend()方法来实现继承。当使用这个方法的时候,会添加一个“superclass”属性。
- Prototype——Class.create()方法和Object.extend()方法来处理对象和“类”。
- Dojo——使用dojo.declare()方法和dojo.extend()方法。
- MooTools——有一个“Class”的自定义类型,用于定义和扩展类。
显而易见,这么多JavaScript框架自定义解决方案明显是不行的,这样反而会更加混乱。JavaScript开发人员需要比当前语法更好更简洁的解决方案。
ECMAScript 6只不过是在你熟知的模式上添加了一层语法糖。例如下面这个例子:
- class MyCustomType {
- constructor(value) {
- this.property = value;
- }
- method() {
- return this.property;
- }
- }
其实这个来创建类和之前提到的使用构造函数来创建对象实例是一样的。唯一的不同点是这个语法更加简洁紧凑。那么该如何实现继承呢:
- class Animal {
- constructor(name) {
- this.name = name;
- }
- sayName() {
- console.log(this.name);
- }
- }
- class Dog extends Animal {
- constructor(name) {
- super(name);
- }
- bark() {
- console.log("Woof!");
- }
- }
与之前那个继承例子相比,这段代码明显清晰明了。使用extends关键字来代替复杂笨重的多步骤继承模式。同时,直接在子类中使用super()方法,无需去声明超类构造函数。
当前,所有的ECMAScript 6类都是基于你已熟知的JavaScript模式。继承的工作模式也一样(原型链接加上调用超类的构造函数),把方法添加到原型上,在构造函数中声明属性。唯一的不同点是你可以输入更少的代码。
因此,当有人讨论ECMAScript 6引入“类”这个概念是否合适时,请注意,这个概念还是很抽象的。它并没有从根本上改变JavaScript的工作原理。它也没引入一个新的东西,它就是简单的在你已使用过的模式上定义一层语法糖。其实这也解决了JavaScript长期存在的一个问题——冗长和混乱的自定义类型语法。但我个人推荐使用 type来代替class。
所以,JavaScript真的需要定义类吗?答案当然是不,JavaScript需要一个简洁的方法来定义自定义类型。刚好,ECMAScript 6里面的“class”提供了这样的方法,并且如果这个“class”能够帮助开发人员轻松地把其他语言转换成JavaScript,那么,这绝对是个好东西!