主要知识点:类声明、类表达式、类的主要要点以及类继续
ES5 中的仿类组织
JS 在 ES5 及更早版本中都不存在类。与类最接近的是:建立一个组织器,然后将要领指派到该组织器的原型上。这类体式格局一般被称为建立一个自定义类型:
function PersonType(name) {
this.name = name;
}
PersonType.prototype.sayName = function() {
console.log(this.name);
};
let person = new PersonType("Nicholas");
person.sayName(); // 输出 "Nicholas"
console.log(person instanceof PersonType); // true
console.log(person instanceof Object); // true
类的声明
基础的类声明
类声明以 class 关键字最先,厥后是类的称号;盈余部份的语法看起来就像对象字面量中的要领简写,而且在要领之间不需要运用逗号:
class PersonClass {
// 等价于 PersonType 组织器,自有属性
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"
console.log(typeof PersonClass.prototype.sayName); // "function"
类声明和函数声明的区分和特征
- 类声明不会被提拔,这与函数定义差别。类声明的行动与 let 类似,因而在顺序的实行抵达声明处之前,类会存在于暂时性死区内。
- 类声明中的一切代码会自动运行在严厉情势下,而且也没法退出严厉情势。
- 类的一切要领都是不可枚举的,这是关于自定义类型的明显变化,后者必须用Object.defineProperty() 才能将要领改变成不可枚举。
- 类的一切要领内部都没有 [[Construct]] ,因而运用 new 来挪用它们会抛出毛病。
- 挪用类组织器时不运用 new ,会抛出毛病。
- 试图在类的要领内部重写类名,会抛出毛病。
用ES5完成适才的类的功用:
// 直接等价于 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;
}());
此例说清楚明了只管不运用新语法也能完成类的任何特征,但类语法明显简化了一切功用的代码。
类表达式
类与函数有类似之处,即它们都有两种情势:声明与表达式。
//声明式
class B {
constructor() {}
}
//匿名表达式
let PersonClass = class {
// 等价于 PersonType 组织器
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"
console.log(typeof PersonClass.prototype.sayName); // "function"
//定名表达式,B能够在外部运用,而B1只能在内部运用
let PersonClass = class PersonClass2 {
// 等价于 PersonType 组织器
constructor(name) {
this.name = name;
}
// 等价于 PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
};
console.log(typeof PersonClass); // "function"
console.log(typeof PersonClass2); // "undefined",只要在类内部才能够接见到
作为一级国民的类
在编程中,能被看成值来运用的就称为一级国民( first-class citizen ),意味着它能作为参数传给函数、能作为函数返回值、能用来给变量赋值。
作为参数传入函数:
function createObject(classDef) {
return new classDef();
}
let obj = createObject(class {
sayHi() {
console.log("Hi!");
}
});
obj.sayHi(); // "Hi!"
经由过程马上挪用类组织函数能够建立单例:
//运用 new 来合营类表达式,并在表达式背面增加括号
let person = new class {
constructor(name) {
this.name = name;
}
sayName() {
console.log(this.name);
}
}("Nicholas");
person.sayName(); // "Nicholas"
接见器属性
自有属性需要在类组织器中建立,而类还许可你在原型上定义接见器属性:
class CustomHTMLElement {
constructor(element) {
this.element = element;
}
get html() {
return this.element.innerHTML;
}
set html(value) {
this.element.innerHTML = value;
}
}
var descriptor = Object.getOwnPropertyDescriptor(CustomHTMLElement.prototype, "html");
console.log("get" in descriptor); // true
console.log("set" in descriptor); // true
console.log(descriptor.enumerable); // false
非类的等价示意以下:
// 直接等价于上个类型
let CustomHTMLElement = (function() {
"use strict";
const CustomHTMLElement = function(element) {
// 确认函数被挪用时运用了 new
if (typeof new.target === "undefined") {
throw new Error("Constructor must be called with new.");
}
this.element = element;
}
Object.defineProperty(CustomHTMLElement.prototype, "html", {
enumerable: false,
configurable: true,
get: function() {
return this.element.innerHTML;
},
set: function(value) {
this.element.innerHTML = value;
}
});
return CustomHTMLElement;
}());
需盘算的成员名
不必运用标识符,而是用方括号来包裹一个表达式:
let methodName = "sayName";
class PersonClass {
constructor(name) {
this.name = name;
}
[methodName]() {
console.log(this.name);
}
}
let me = new PersonClass("Nicholas");
me.sayName(); // "Nicholas"
接见器属性能以雷同体式格局运用需盘算的称号,就像如许:
let propertyName = "html";
class CustomHTMLElement {
constructor(element) {
this.element = element;
}
get [propertyName]() {
return this.element.innerHTML;
}
set [propertyName](value) {
this.element.innerHTML = value;
}
}
生成器要领
你已学会怎样在对象字面量上定义一个生成器:只要在要领称号前附加一个星号( * )。这一语法对类一样有用,许可将任何要领变成一个生成器:
class MyClass {
*createIterator() {
yield 1;
yield 2;
yield 3;
}
}
let instance = new MyClass();
let iterator = instance.createIterator();
静态成员
直接在组织器上增加分外要领来模仿静态成员,这在 ES5 及更早版本中是另一个通用的情势:
function PersonType(name) {
this.name = name;
}
// 静态要领
PersonType.create = function(name) {
return new PersonType(name);
};
// 实例要领
PersonType.prototype.sayName = function() {
console.log(this.name);
};
var person = PersonType.create("Nicholas");
ES6 的类简化了静态成员的建立,只要在要领与接见器属性的称号前增加正式的 static 标注:
class PersonClass {
// 等价于 PersonType 组织器
constructor(name) {
this.name = name;
}
// 等价于 PersonType.prototype.sayName
sayName() {
console.log(this.name);
}
// 等价于 PersonType.create
static create(name) {
return new PersonClass(name);
}
}
let person = PersonClass.create("Nicholas");
和一般要领不一样的是,static润饰的要领不能在实例中接见,只能在类中直接接见。