类的声明
类在 ES6
中最简单的形式就是类声明,它看起来很像其他语言中的类。
基本的类声明
类声明以 class
关键字开始,其后是类的名称;剩余部分的语法看起来就像对象字面量中的方法简写,并且在方法之间不需要使用逗号。作为范例,此处有个简单的类声明:
class PersonClass {
// equivalent of the PersonType constructor
constructor(name) {
this.name = name;
}
// equivalent of PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
}
let person = new PersonClass("Nicholas");
person.sayName(); // outputs "Nicholas"
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
之外的方法名称则没有特别的含义,因此可以随你高兴自由添加方法。
自有属性(Own properties):该属性出现在实例上而不是原型上,只能在类的构造器或方法内部进行创建。在本例中, |
有趣的是,相对于已有的自定义类型声明方式来说,类声明仅仅是以它为基础的一个语法糖。PersonClass
声明实际上创建了一个拥有 constructor
方法及其行为的函数,这也是 typeof PersonClass
会得到 "function"
结果的原因。 此例中的 sayName()
方法最终也成为 PersonClass.prototype
上的一个方法,类似于上个例子中 sayName()
与 PersonType.prototype
之间的关系。这些相似处允许你把自定义类型与类混合使用,而不必太担忧到底该用哪个。
为何要使用类的语法
尽管类与自定义类型之间有相似性,但仍然要记住一些重要的区别:
-
类声明不会被提升,这与函数定义不同。类声明的行为与 let 相似,因此在程序的执行到达声明处之前,类会存在于暂时性死区内。
-
类声明中的所有代码会自动运行在严格模式下,并且也无法退出严格模式。
-
类的所有方法都是不可枚举的,这是对于自定义类型的显著变化,后者必须用
Object.defineProperty()
才能将方法改变为不可枚举。 -
类的所有方法内部都没有
[[Construct]]
,因此使用new
来调用它们会抛出错误。 -
调用类构造器时不使用
new
,会抛出错误。 -
试图在类的方法内部重写类名,会抛出错误。
这样看来,上例中的 PersonClass
声明实际上就直接等价于以下未使用类语法的代码:
// direct equivalent of PersonClass
let PersonType2 = (function() {
"use strict";
const PersonType2 = function(name) {
// make sure the function was called with 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() {
// make sure the method wasn't called with 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;
}());
首先要注意这里有两个 PersonType2
声明:一个在外部作用域的 let
声明,一个在 IIFE
内部的 const
声明。这就是为何类的方法不能对类名进行重写、而类外部的代码则被允许。构造器函数检查了 new.target
,以保证被调用时使用了 new
,否则就抛出错误。接下来,sayName()
方法被定义为不可枚举,并且此方法也检查了 new.target
,它则要保证在被调用时没有使用 new
。最后一步是将构造器函数返回出去。
此例说明了尽管不使用新语法也能实现类的任何特性,但类语法显著简化了所有功能的代码。
不变的类名 只有在类的内部,类名才被视为是使用
在此代码中,类构造器内部的 |