深入理解Typescript系列-类

543 阅读3分钟

这是我参与 8 月更文挑战的第 14 天,活动详情查看: 8月更文挑战

前言

当大家看到typescript(ts) 中的类(class)时候,可能好多人都会想起面向对象,对的,面向对象是许多后台的一种编程思想,比如正统的面向对象语言java。但是本文不讨论面向对象,值讨论ts 中 class 新增的语法,和一些使用方法以及注意事项。

ES6中的类

说到TS的类,我们就不得不先说ES6的类了。

在ECMAScript 2015之前,我们需要通过构造函数、prototype等属性来模拟一个类,类似这样:

function Cat(name, color){
    this.name = name;
    this.color = color;
}
 
Cat.prototype.info = function (){
    return this.name + this.color;
}

从ECMAScript 2015,也就是ECMAScript 6开始,JavaScript程序员将能够使用基于类的面向对象的方式。ES6引入了Class(类)这个概念,通过class关键字可以定义类。该关键字的出现使得其在对象写法上更加清晰,更像是一种面向对象的语言。如果将之前的代码改为ES6的写法就会是这个样子:

class Cat{
    name = null;
    color = null;
    constructor(name, color) {
        this.name = name;
        this.color = color;
    }

    info() {
        return this.name + this.color;
    }
}

constructor方法如果没有显式定义,会隐式生成一个constructor方法。所以即使你没有添加构造函数,构造函数也是存在的。constructor方法默认返回实例对象this,但是也可以指定constructor方法返回一个全新的对象,让返回的实例对象不是该类的实例。

constructor中定义的属性可以称为实例属性(即定义在this对象上),constructor外声明的属性都是定义在原型上的,可以称为原型属性(即定义在class上)。hasOwnProperty()函数用于判断属性是否是实例属性。其结果是一个布尔值, true说明是实例属性,false说明不是实例属性。in操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例中还是原型中。

TS中的类

比起ES6中的类,在TS中,多了一些类型定义和private、public等关键字。

class Cat{
    private name: string = null;
    private color: string = null;
    constructor(name: string, color: string) {
        this.name = name;
        this.color = color;
    }

    public info(): string {
        return this.name + this.color;
    }
}

注意:我们在公开的info方法中访问了this,会指向类的实例,因为javascript太灵活了,如果这么写,就会出问题:

class Cat{
    private name: string = null;
    private color: string = null;
    constructor(name: string, color: string) {
        this.name = name;
        this.color = color;
    }

    public info(): string {
        return this.name + this.color;
    }
}

const cat1 = new Cat();
cat1.info() // ok

const { info } = new Cat();
info() // error

因为解构赋值,导致info的this指向变为了全局,当然并不推荐以上的写法,如果非要这么写,需要需要改造一下类:

class Cat{
    private name: string = null;
    private color: string = null;
    constructor(name: string, color: string) {
        this.name = name;
        this.color = color;

        this.info = this.info.bind(this)
    }

    public info(): string {
        return this.name + this.color;
    }
}

const cat1 = new Cat();
cat1.info() // ok

const { info } = new Cat();
info() // ok

通过bind显式this绑定,完成了this指向永远是当前实例,当然我们这里也可以用箭头函数。

class继承

在TypeScript里,我们可以使用常用的面向对象模式。 基于类的程序设计中一种最基本的模式是允许使用继承来扩展现有的类。


class Animal {
    public info(): string {
        return this.name + this.color;
    }
}

class Cat extends Animal {
    private name: string = null;
    private color: string = null;
    constructor(name: string, color: string) {
        this.name = name;
        this.color = color;

        this.info = this.info.bind(this)
    }
}

Cat作为Animal的派生类,通过extends关键字完成。

小结

TS的类的出现时间是早于ES6的类的,所以官方文档在这点上并没有做区分。希望可以通过本文的剖析,可以彻底理清ES6的类和TS的类有哪些异同。