仿类结构
先创建一个构造器函数,然后在这个函数的原型中存储方法,这个构造器函数生成的实例从原型继承了存储的方法。
function Con(n) {
this.n = n;
}
Con.prototype.fun = function () {
console.log(this.n);
};
let c = new Con("zzz");
c.fun(); // "zzz"
类的声明
class Name {
constructor(name) {
this.name = name;
}
say() {
console.log(this.name);
}
}
let n = new Name("xqy");
n.say(); // "xqy"
typeof Name // "function"
声明的类本质上还是一个函数,类的方法依然作为属性存储在这个函数的原型中。
自有属性(own properties)是定义在实例而不是原型上的属性,一般在类的构造器中进行声明。
类声明不会发生自举,和 let
定义变量类似。
类声明默认在严格模式下执行,不会退出严格模式。
类中定义的方法(不包括构造器)不可枚举。
类中的方法如果没有 [[Construct]] 属性,则不能使用 new 调用,使用 new 调用会抛出错误。
类中的构造器只能使用 new 调用,不使用 new 调用会抛出错误。
类名在类的内部视为 const
变量,不可更改,否则抛出错误。但是类名在类的外部视为 let
定义的变量,可以更改。
类表达式
与上面类声明等价的类表达式为
let Name = class {
constructor(name) {
this.name = name;
}
say() {
console.log(this.name);
}
}
创建有名称的类表达式
let Name = class Inner {
constructor(name) {
this.name = name;
}
say() {
console.log(this.name);
}
}
typeof Name // "function"
typeof Inner // undefined
Inner 是具名类表达式的名称,这个名称相当于是在类的内部定义,在类的外部无法访问,所以返回结果为 undefined。
类
类可以当作值来使用,也可以作为函数参数、函数返回值,给变量赋值。
立即调用类构造器创建单例
let n = new class {
constructor(name) {
this.name = name;
}
say() {
console.log(this.name);
}
}("xyz");
n.say(); // "xyz"
在创建类的时候传递参数实例化该类,该类的自有属性已经被固定,同时该类也只有一个实例和一个引用。
访问器属性
访问器属性实质上是在原型中定义的。下面的代码片段中 getValue()
和 setValue(v)
方法都是定义在 Num 的原型中。value 是自有属性,定义在实例上。
class Num {
constructor(v) {
this.value = v;
}
get value() {
return this.value;
}
set value(v) {
this.value = v;
}
}
计算的成员名
类中的方法名和访问器属性可以使用需计算的名称。
let mName = "say",
n = "num";
class Num {
constructor(name, v) {
this.name = name;
this.value = v;
}
[mName]() {
console.log(this.name);
}
get [n]() {
return this.value;
}
set [n](v) {
this.value = v;
}
}
let n = Num("xyz", 25);
n.say(); // "xyz"
生成器方法
在类中可以创建生成器。为 Symbol.iterator 属性定义一个生成器作为默认的迭代器。
class Coll {
constructor() {
this.items = [];
}
*[Symbol.iterator]() {
yield *this.items.values();
}
}
静态成员
类本身具有的方法或属性称为静态成员,静态成员不属于类的原型。访问静态成员不能使用类实例而需要直接使用类。类成员前面添加 static 表示该成员是静态的,构造器不允许是静态成员。
class Name {
constructor(name) {
this.name = name;
}
// 等价于 Name.prototype.say()
say() {
console.log(this.name);
}
// 等价于 Name.create()
static create(name) {
return new Name(name);
}
}
let n = Name.create("xyz");
派生类继承
类的继承使用 extends
class Rect {
constructor(length, width) {
this.length = length;
this.width = width;
}
getArea() {
return this.length * this.width;
}
}
class Square extends Rect {
constructor(length) {
super(length, length);
}
}
let s = new Square(3);
s.getArea(); // 9
子类中如果自定义构造器,则必须在构造器中调用 super()
初始化基类,super 表示基类。如果不定义构造器,则默认构造器会在用 new 创建子类时,将传入子类的所有参数作为 super 的参数调用 super()
。
只能在子类中使用 super()
。在构造器中必须在 super()
语句调用之后才能访问 this,因为 this 需要 super()
调用之后才会初始化。构造器中不使用 super()
的唯一方式是构造器返回一个对象。
子类中定义与基类同名的方法时,子类中的方法会屏蔽基类中的同名方法。如果此时需要调用基类中被屏蔽的方法,使用 super.method()
访问。
类继承中,基类中的静态成员会被子类继承。
具有 [[Construct]] 属性和原型的函数可以作为基类使用 extends 被继承。任何结果(返回值)为前述函数的表达式(函数)可以位于 extends 后面使用。
继承内置对象时,this 的值先由基类创建,再被子类修改。子类的行为默认与基类(内置对象)一致。返回内置对象实例的方法如果接收的参数为继承自内置对象的子类,则该方法返回的实例类型为这个子类。
Symbol.species 是返回一个函数的静态访问器属性。类的方法(构造器除外)需要创建一个新实例时,会调用 Symbol.species 返回的函数作为构造器创建这个类实例。
内置类型 Array、ArrayBuffer、Map、Promise、RegExp、Set、类型化数组的默认 Symbol.species 返回的是 this。
class Base {
constructor(v) {
this.value = v;
}
// this.constructor[Symbol.species] 为 Symbol.species 返回的函数
duplicate() {
return new this.constructor[Symbol.species](this.value);
}
static get [Symbol.species]() {
return this;
}
}
class Sub1 extends Base {}
class Sub2 extends Base {
static get [Symbol.species]() {
return Base;
}
}
let sub1 = new Sub1("sub1")
ds1 = sub1.duplicate(); // 创建的对象类型为 Sub1
let sub2 = new Sub2("sub2"),
ds2 = sub2.duplicate(); // 创建的对象类型为 Base
Symbol.species 返回的函数决定类的方法创建新实例时该实例的类型。
构造器中使用 new.target
使用 new 创建类实例时, new.target 的值为 new 的目标类型,也就是类。所以当使用 new 实例化类时,在构造器中 new.target 总存在值。
在基类的构造器中使用了 new.target 的前提下,用 new 实例化子类时,子类的构造器会调用基类的构造器,此时 new.target 的值是子类而不是基类,可能基类中 new.target 的使用在这种情况下会出现意料之外的结果。
参考
[1] Zakas, Understanding ECMAScript 6, 2017.
标签:name,09,constructor,let,基类,new,class,UES From: https://www.cnblogs.com/xdreamc/p/16552483.html