3
主要知识点:类声明、类表达式、类的重要要点以及类继承
图片描述

《深入理解ES6》笔记 目录

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"

类声明和函数声明的区别和特点

  1. 类声明不会被提升,这与函数定义不同。类声明的行为与 let 相似,因此在程序的执行到达声明处之前,类会存在于暂时性死区内。
  2. 类声明中的所有代码会自动运行在严格模式下,并且也无法退出严格模式。
  3. 类的所有方法都是不可枚举的,这是对于自定义类型的显著变化,后者必须用Object.defineProperty() 才能将方法改变为不可枚举。
  4. 类的所有方法内部都没有 [[Construct]] ,因此使用 new 来调用它们会抛出错误。
  5. 调用类构造器时不使用 new ,会抛出错误。
  6. 试图在类的方法内部重写类名,会抛出错误。

用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修饰的方法不能在实例中访问,只能在类中直接访问。


sevencui
355 声望32 粉丝