资讯专栏INFORMATION COLUMN

深入理解ES6笔记(九)JS的类(class)

xinhaip / 712人阅读

摘要:主要知识点类声明类表达式类的重要要点以及类继承深入理解笔记目录中的仿类结构在及更早版本中都不存在类。与类最接近的是创建一个构造器,然后将方法指派到该构造器的原型上。调用类构造器时不使用,会抛出错误。

主要知识点:类声明、类表达式、类的重要要点以及类继承

《深入理解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"
类声明和函数声明的区别和特点

类声明不会被提升,这与函数定义不同。类声明的行为与 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修饰的方法不能在实例中访问,只能在类中直接访问。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/96967.html

相关文章

  • 深入理解ES6笔记—— JavaScript中的类class(9)

    摘要:新建一个类该函数返回一个类的实例给函数传入通过立即调用类构造函数可以创建单例。派生类是指继承自其它类的新类。在构造函数中访问之前要调用,负责初始化。在构造函数中使用通常表示当前的构造函数名。 ES5中的近类结构 ES5以及之前的版本,没有类的概念,但是聪明的JavaScript开发者,为了实现面向对象,创建了特殊的近类结构。 ES5中创建类的方法:新建一个构造函数,定义一个方法并且赋值...

    gggggggbong 评论0 收藏0
  • 深入理解ES6笔记—— JavaScript中的类class(9)

    摘要:新建一个类该函数返回一个类的实例给函数传入通过立即调用类构造函数可以创建单例。派生类是指继承自其它类的新类。在构造函数中访问之前要调用,负责初始化。在构造函数中使用通常表示当前的构造函数名。 ES5中的近类结构 ES5以及之前的版本,没有类的概念,但是聪明的JavaScript开发者,为了实现面向对象,创建了特殊的近类结构。 ES5中创建类的方法:新建一个构造函数,定义一个方法并且赋值...

    Jason 评论0 收藏0
  • 深入理解ES6笔记——导读

    摘要:最近买了深入理解的书籍来看,为什么学习这么久还要买这本书呢主要是看到核心团队成员及的创造者为本书做了序,作为一个粉丝,还是挺看好这本书能给我带来一个新的升华,而且本书的作者也非常厉害。 使用ES6开发已经有1年多了,以前看的是阮一峰老师的ES6教程,也看过MDN文档的ES6语法介绍。 最近买了《深入理解ES6》的书籍来看,为什么学习ES6这么久还要买这本书呢?主要是看到Daniel A...

    Godtoy 评论0 收藏0

发表评论

0条评论

xinhaip

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<