ES6 Demos(九) Introducing JavaScript Classes(JS的类)
Demo1: ES5 中的仿类结构
Demo2: 类的声明
// 类声明以 class 关键字开始,其后是类的名称;剩余部分的语法看起来就像对象字面量中的方法简写,并且在方法之间不需要使用逗号.
class PersonClass {
//语法糖,相当于创建了一个构造函数用于对象属性,使得每个实例的属性都私有化
constructor(name) {
this.name = name;
}
//相当于PersonType.prototype.sayName
sayName(){
console.log(this.name);
}
}
let person = new PersonClass("Nicholas");
person.sayName(); // 输出 "Nicholas"
console.log(person instanceof PersonClass); // true
console.log(person instanceof Object); // true
console.log(typeof PersonClass); // "function", 语法糖说明class本质是一个函数
console.log(typeof PersonClass.prototype.sayName); // "function"
尽管类与自定义类型之间有相似性,但仍然要记住一些重要的区别:
1. 类声明不会被提升,这与函数定义不同。类声明的行为与 let 相似,因此在程序的执行到达声明处之前,类会存在于暂时性死区内。
2. 类声明中的所有代码会自动运行在严格模式下,并且也无法退出严格模式。
3. 类的所有方法都是不可枚举的,这是对于自定义类型的显著变化,后者必须用Object.defineProperty() 才能将方法改变为不可枚举。
4. 类的所有方法内部都没有 [[Construct]] ,因此使用 new 来调用它们会抛出错误。
5. 调用类构造器时不使用 new ,会抛出错误。
6. 试图在类的方法内部重写类名,会抛出错误。
// 不变的类名
// 只有在类的内部,类名才被视为是使用 const 声明的。这意味着你可以在外部重写类名,但不能在类的方法内部这么做。例如:
class Foo {
constructor() {
Foo = "bar"; // 执行时抛出错误
}
}
// 但在类声明之后没问题
Foo = "baz";
// 在此代码中,类构造器内部的 Foo 与在类外部的 Foo 是不同的绑定。内部的 Foo 就
// 像是用 const 定义的,不能被重写,当构造器尝试使用任何值重写 Foo 时,都会抛出
// 错误。但由于外部的 Foo 就像是用 let 声明的,你可以随时重写类名。
Demo3: 类的表达式
使用类声明还是类表达式,主要是代码风格问题。相对于函数声明与函数表达式之间的区别,类声明与类表达式都不会被提升,因此对代码运行时的行为影响甚微。
let PersonClass2 = class {
// 等价于 PersonType 构造器
constructor(name) {
this.name = name;
}
// 等价于 PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
};
let person2 = new PersonClass2("Nicholas");
person.sayName(); // 输出 "Nicholas"
console.log(person instanceof PersonClass2); // true
console.log(person instanceof Object); // true
console.log(typeof PersonClass2); // "function"
console.log(typeof PersonClass2.prototype.sayName); // "function"
Demo4: 具名类表达式
let PersonClass3 = class PersonClass4 {
// 等价于 PersonType 构造器
constructor(name) {
this.name = name;
}
// 等价于 PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
};
console.log(typeof PersonClass3); // "function"
//PersonClass4只能在类内部使用
console.log(typeof PersonClass4); // "undefined"
Demo5: 类可以用来作为参数传入函数
function createObject(classDef) {
return new classDef();
}
let obj = createObject(class {
sayHi() {
console.log("Hi!");
}
});
obj.sayHi(); // "Hi!"
Demo6: 单类,创建了一个匿名类表达式,并立即执行了它
let person5 = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}("Nicholas");
person5.sayName(); // "Nicholas"
Demo7: 访问器属性(暂略,我没看懂有什么用处)
Demo8: 类中可计算的成员名
let methodName = "sayName";
class PersonClass6 {
constructor(name) {
this.name = name;
}
[methodName]() {
console.log(this.name);
}
}
let me = new PersonClass6("Nicholas");
me.sayName(); // "Nicholas"
Demo9: 类的静态成员,只要在方法的名称前添加正式的 static 标注
class PersonClass7 {
// 等价于 PersonType 构造器
constructor(name) {
this.name = name;
}
// 等价于 PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
// 等价于 PersonType.create
static create(name) {
return new PersonClass7(name);
}
}
//静态成员不能用实例来访问,你始终需要直接用类自身来访问它们。
let person7 = PersonClass7.create("Nicholas+create");
person7.sayName(); //Nicholas+create
Demo10: ES6来进行类继承
class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
getArea() {
return this.length * this.width;
}
}
class Square extends Rectangle {
constructor(length) {
//因为 Square 构造器只需要单个参数,因此最好手动定义构造器
super(length, length);
}
}
var square = new Square(4);
console.log(square.getArea()); // 16
使用 super() 时需牢记以下几点:
1. 你只能在派生类中使用 super() 。若尝试在非派生的类(即:没有使用 extends关键字的类)或函数中使用它,就会抛出错误。
2. 在构造器中,你必须在访问 this 之前调用 super() 。由于 super() 负责初始化this ,因此试图先访问 this 自然就会造成错误。
3. 唯一能避免调用 super() 的办法,是从类构造器中返回一个对象。
Demo11: 继承静态成员
class Rectangle {
constructor(length, width) {
this.length = length;
this.width = width;
}
getArea() {
return this.length * this.width;
}
static create(length, width) {
return new Rectangle(length, width);
}
}
class Square extends Rectangle {
constructor(length) {
// 与 Rectangle.call(this, length, length) 相同
super(length, length);
}
}
var rect = Square.create(3, 4);
// 在此代码中,一个新的静态方法 create() 被添加到 Rectangle 类中。通过继承,该方法会
// 以 Square.create() 的形式存在,并且其行为方式与 Rectangle.create() 一样。
console.log(rect instanceof Rectangle); // true
console.log(rect.getArea()); // 12
console.log(rect instanceof Square); // false