TypeScript面向对象超详细分析

目录
  • 1 面向对象原则
  • 2 TypeScript类
    • 2.1 类的定义
    • 2.2 创建实例对象
  • 3 类的继承
  • 4 static关键字
  • 5 抽象类和抽象方法
  • 6 类属性权限修饰符
    • 6.1 public(公有的)
    • 6.2 protected(受保护的)
    • 6.3 private(受保护的)
    • 6.4 readonly(只读)
  • 7 存取器

1 面向对象原则

  • 单一原则:一个类只负责一个职责。
  • 里氏替换原则:子类可以在任何地方替换它的父类。
  • 依赖倒置原则:代码要依赖于抽象的类,而不要依赖于具体的类;要针对接口或抽象类编程,而不是针对具体类编程。
  • 接口隔离原则:提供尽可能小的单独接口,而不要提供大的总接口。暴露行为让后面的实现类知道的越少越好。
  • 迪米特法则:尽量降低类与类之间的耦合。
  • 开闭原则:面向扩展开放,面向修改关闭。
  • 组合/聚合复用原则:尽量使用合成/聚合达到复用,尽量少用继承。原则: 一个类中有另一个类的对象。

2 TypeScript类

TypeScript 是面向对象的 JavaScript。类描述了所创建的对象共同的属性和方法。TypeScript 支持面向对象的所有特性。

2.1 类的定义

TypeScript使用class关键字定义类。

class 类名 {
    // 具体代码
}

类可以包含以下几个模块(类的数据成员):

  • 字段(属性):字段是类里面声明的变量。字段表示对象的有关数据。
  • 构造函数:类实例化时调用,可以为类的对象分配内存。
  • 方法:方法为对象要执行的操作。

那么一个类的结构大致如下:

class 类名 {
    // 字段
    属性名: 类型;

// 构造函数
    constructor(参数: 类型) {
        this.属性名 = 参数;
    }

// 方法
    方法名() {
        // 具体方法
    }
}

示例代码:创建一个Car类

class Car {
    // 字段
    engine: string;
    // 构造函数
    constructor(engine: string) {
        this.engine = engine;
    }
    // 方法
    disp(): void {
        console.log("发动机为:" + this.engine);
    }
}

2.2 创建实例对象

在类中使用new关键字来实例化类的对象,语法格式如下:

var 对象名 = new 类名([参数]);

类中的字段和方法可以使用.来访问:

// 访问属性
对象名.字段名;

// 访问方法
对象名.方法名();

示例代码:为Car类创建一个实例对象,并访问字段和方法

class Car {
    engine: string;
    constructor(engine: string) {
        this.engine = engine;
    }
    disp(): void {
        console.log("发动机为:" + this.engine);
    }
}
let bc = new Car("引擎1");
console.log(bc.engine); // 引擎1
bc.disp(); // 发动机为:引擎1

3 类的继承

TypeScript 支持继承类,即我们可以在创建类的时候继承一个已存在的类,这个已存在的类称为父类,继承它的类称为子类。

类继承使用关键字extends,子类除了不能继承父类的私有成员(方法和属性)和构造函数,其他的都可以继承。TypeScript 一次只能继承一个类,不支持继承多个类,但 TypeScript 支持多重继承(A 继承 B,B 继承 C)。

示例代码:

// 定义一个动物类Animal作为父类
class Animal {
    name: string; // 字段
    constructor(name: string) { // 构造函数
        this.name = name;
    }
    eat(): void {
        console.log(this.name + "在吃");
    }
}
// 定义一个Cat类作为子类,继承父类Animal
class Cat extends Animal {
    // 定义自己独有的方法叫
    say() {
        console.log("喵喵喵");
    }
}
// 创建Cat类的实例对象
let cat1 = new Cat("橘猫");
// 访问父类的eat()方法
cat1.eat(); // 橘猫在吃
// 访问自己的say()方法
cat1.say(); // 喵喵喵

继承类的方法重写(overwrite):又称为方法覆盖。类继承后,子类可以对父类的方法重新定义,这个过程称之为方法的重写。示例代码如下:

// 定义一个动物类Animal作为父类
class Animal {
    eat(): void {
        console.log("在吃饭");
    }
}
// 定义一个Cat类作为子类,继承父类Animal
class Cat extends Animal {
    // 对父类的方法进行重写
    eat(): void {
        console.log("猫猫在吃鱼");
    }
}
// 创建Cat类的实例对象
let cat1 = new Cat();
cat1.eat(); // 猫猫在吃鱼

在类中,使用super关键字可以引用父类的属性和方法,super就表示父类。例如,使用super调用父类的普通方法:

// 定义一个动物类Animal作为父类
class Animal {
    eat(): void {
        console.log("在吃饭");
    }
}
// 定义一个Cat类作为子类,继承父类Animal
class Cat extends Animal {
    eat(): void {
        super.eat(); // 调用父类的eat()方法
    }
}
// 创建Cat类的实例对象
let cat1 = new Cat();
cat1.eat(); // 在吃饭

使用super调用父类的构造方法时,super必须写在子类构造函数的第一条语句,如果父类的构造函数中有参数,那么在super中也要将该参数传递进去。示例代码如下:

// 定义一个动物类Animal作为父类
class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    eat(): void {
        console.log("在吃饭");
    }
}
// 定义一个Cat类作为子类,继承父类Animal
class Cat extends Animal {
    age: number;
    constructor(name: string, age: number) {
        super(name); // 必须写在第一条语句
        this.age = age;
    }
}

4 static关键字

static关键字用于定义类的数据成员(属性和方法)为静态的,静态成员可以直接通过类名调用。

示例代码:

class StaticMem {
    static num: number;
    static disp(): void {
        console.log("num值为:" + StaticMem.num);
    }
}
StaticMem.num = 100; // 初始化静态变量
StaticMem.disp(); // 调用静态方法
// num值为:100

5 抽象类和抽象方法

abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。abstract抽象方法只能放在抽象类里面。抽象类和抽象方法用来定义标准。

抽象类不能用来创建对象,是专门用来被继承的类。

示例:定义一个抽象类和抽象方法

abstract class Animal { // 定义一个抽象类
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    abstract eat(): void; // 定义一个抽象方法
}

如果对抽象方法进行了具体的实现,那么就会报错:Method ‘eat’ Cannot have an implementation because it is marked abstract。

并且抽象类并不能被实例化,否则也会报错:Cannot create an instance of an abstract class。

如果某个类要继承抽象类,那么这个类要实现抽象类中定义的所有方法,否则也会报错。

例如,设计一个Cat继承Animal抽象类,如下所示,如果Cat类中什么方法都没有,就会出现提示,Cat类并没有实现抽象类中的抽象方法eat。

因此在继承时,在Cat类中必须实现eat方法:

abstract class Animal { // 定义一个抽象类
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    abstract eat(): void;
}
class Cat extends Animal {
    eat(): void {
        console.log(this.name + "吃鱼");
    }
}

6 类属性权限修饰符

6.1 public(公有的)

public表示公有的、公开的,公有成员可以被任何地方访问,默认可见性

class Animal {
    public move() { // 使用public修饰符表示公开的成员
        console.log("走");
    }
}
var p = new Animal();
p.move(); // 走
class Cat extends Animal {
    bark() {
        console.log("喵喵喵");
    }
}
var c = new Cat();
c.move(); // 走
c.bark(); // 喵喵喵

在类属性或方法前面添加public关键字,来修饰该属性或方法是公有的。因为public是默认可见性,所以可以直接省略。

6.2 protected(受保护的)

protected表示受保护的,仅对其声明所在类和子类中(非实例对象)可见。在类属性或方法前面添加protected关键字,可以修饰属性或方法是受保护的。在子类的方法内部可以通过this来访问父类中受保护的成员,但是对实例不可见。

例如,在下面的代码中,move方法被protected修饰了,对Animal类创建实例,该实例并不能访问到move方法。

class Animal {
    protected move() {
        console.log("走");
    }
}
var p = new Animal();
p.move(); // 不能访问到move方法

如果设计一个Cat类继承了Animal类,那么在Cat类的内部可以通过this访问到move方法。

class Animal {
    protected move() {
        console.log("走");
    }
}
class Cat extends Animal {
    bark() {
        this.move();
    }
}
var c = new Cat();
c.bark(); // 走

6.3 private(受保护的)

private表示私有的,只在当前类中可见,对实例对象和子类都是不可见的。在类属性或方法前面添加private关键字,来修饰该属性或方法是私有的。

例如,在Animal类中,move方法被private修饰,因此实例对象a无法对move方法进行调用

class Animal {
    private move() {
        console.log("走");
    }
}
var a = new Animal();
a.move(); // 报错

但是它可以在自己类内部的方法中进行调用:

class Animal {
    private move() {
        console.log("走");
    }
    eat() {
        this.move(); // 使用正确不报错
    }
}

move方法同样也不能被子类所调用,对子类不可见:

class Animal {
    private move() {
        console.log("走");
    }
}
class Cat extends Animal {
    bark() {
        this.move(); // 无法调用,报错
    }
}

6.4 readonly(只读)

readonly表示只读,用来防止在构造函数之外对属性进行赋值。使用readonly关键字只能修饰属性不能修饰方法。接口或者{}表示的对象类型,也可以使用readonly

例如,在下面的代码中,age被readonly修饰,是只读属性,因此在实例化时再对age进行修改就会报错。

class Person {
    readonly age: number = 18;
}
var p = new Person();
p.age = 19; // 报错,只读属性不能修改

如果不加readonly就可以修改:

class Person {
    age: number = 18;
}
var p = new Person();
p.age = 19;
console.log(p.age); // 19

7 存取器

TypeScript支持通过getters/setters来截取对对象成员的访问。

在类中设置某个属性语法为:

set 属性名(参数): [类型]{
    this.属性 = 参数;
}

获取某个属性语法为:

get 属性名(): [类型]{
    return this.属性;
}

示例代码:

class Person {
    // 将tall属性设置为私有的,这样无法通过实例来修改
    // 私有变量约定用下划线开头
    private _tall: number;
    constructor(tall: number) {
        this._tall = tall;
    }
    // 获取tall的值
    get height(): number {
        return this._tall;
    }
    // 设置tall的值
    set height(tall: number) {
        this._tall = tall;
    }
}
var p = new Person(160);
p.height = 180;
console.log(p.height); // 180

到此这篇关于TypeScript面向对象超详细分析的文章就介绍到这了,更多相关TypeScript面向对象内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Typescript tipe freshness 更严格对象字面量检查

    目录 引言 严格检查存在缺点 具体规则而言 引言 Typescript 是结构化的类型系统,那么对于对象来说,如果 A 具有 B 的所有属性,并且属性对应的类型相匹配,那么 A 就能赋值给 B type A = { name:string; age:number; } type B = { name:string } declare let a:A declare let b: B b = a 但是其中有一个例外的情况,会强制 a.b 具有完全相同的结构,被称为 freshness,也被称为更严

  • JS TypeScript的Map对象及联合类型实战

    目录 一.TypeScript的Map对象 1.1迭代 Map 二.TypeScript 联合类型 2.1扩展知识 2.2总结 一.TypeScript的Map对象 类型脚本映射对象. map对象保存键值对,可以记住键的原始插入顺序.任何值(对象或原始值)都可以用作键或值.Map是ES6中引入的新数据结构. Typescript使用地图类型和new关键字创建Map: let myMap = new Map(); 初始化映射,可以以数组的形式传入键值对: let myMap = new Map([

  • TypeScript 中如何限制对象键名的取值范围

    当我们使用 TypeScript 时,我们想利用它提供的类型系统限制代码的方方面面,对象的键值,也不例外. 譬如我们有个对象存储每个年级的人名,类型大概长这样: type Students = Record<string, string[]>; 理所当然地,数据就是长这样: const students: Students = { Freshman: ["David", "John"], sophomore: [], Junior: ["Lil

  • TypeScript遍历对象属性的问题

    目录 一.问题 二.解决办法 1. 把对象声明as any 2. 给对象声明一个接口 3. 使用泛型 4. 使用keyof 一.问题 比如下面的代码: type Animal = { name: string; age: number } const animal:Animal={ name:"dog", age:12 } function test(obj:Animal) { for (let k in obj) { console.log(obj[k]).//这里出错 } } te

  • 如何在TypeScript中正确的遍历一个对象

    目录 JavaScript TypeScript for...in Object.keys Object.entries 思考 总结 JavaScript 在讲解用 Ts 遍历一个对象之前, 我们先说说 在 Js 中怎么实现, for...in.Object.keys, 一个简单的例子: // for...in const obj = { name: 'itsuki', address: 'hangzhou', }; for (const key in obj) { console.log(ke

  • Java超详细分析继承与重写的特点

    概念:继承是面向对象语法三大特征之一,继承可以降低代码的沉余度,提高编程的效率.通过继承子类可以随意调用父类中的某些属性与方法,一个子类只能继承一个父类,一个父类可以被多个子类继承.它就好比与我们显示生活中孩子继承父亲的财产.重写的好处在于子类可以根据需要,定义特定于自己的行为. 也就是说子类能够根据需要实现父类的方法,就好比金毛与哈士奇他的特征都是来自狗,仓鼠与松鼠他们他们的特征来自老鼠,而他们身上的不同属于基因突变就相当于重写 继承的特点: 1):java中只支持单根继承,即一个类只能有一个

  • Redis对象与redisObject超详细分析源码层

    目录 一.对象 二.对象的类型及编码 redisObject 结构体 三.不同对象编码规则 四.redisObject结构各字段使用范例 4.1 类型检查(type字段) 4.2 多态命令的实现(encoding) 4.3 内存回收和共享对象(refcount) 4.4 对象的空转时长(lru) 五.对象在源码中的使用 5.1 字符串对象 5.1.1字符串对象创建 5.1.2 字符串对象编码 5.1.3 字符串对象解码 5.1.4 redis对象引用计数及自动清理 六.总结 以下内容是基于Red

  • Java超详细分析泛型与通配符

    目录 1.泛型 1.1泛型的用法 1.1.1泛型的概念 1.1.2泛型类 1.1.3类型推导 1.2裸类型 1.3擦除机制 1.3.1关于泛型数组 1.3.2泛型的编译与擦除 1.4泛型的上界 1.4.1泛型的上界 1.4.2特殊的泛型上界 1.4.3泛型方法 1.4.4类型推导 2.通配符 2.1通配符的概念 2.2通配符的上界 2.3通配符的下界 题外话: 泛型与通配符是Java语法中比较难懂的两个语法,学习泛型和通配符的主要目的是能够看懂源码,实际使用的不多. 1.泛型 1.1泛型的用法

  • 非常适合新手学生的Java线程池超详细分析

    目录 线程池的好处 创建线程池的五种方式 缓存线程池CachedThreadPool 固定容量线程池FixedThreadPool 单个线程池SingleThreadExecutor 定时任务线程池ScheduledThreadPool ThreadPoolExecutor创建线程池(十分推荐) ThreadPoolExecutor的七个参数详解 workQueue handler 如何触发拒绝策略和线程池扩容? 线程池的好处 可以实现线程的复用,避免重新创建线程和销毁线程.创建线程和销毁线程对

  • C++超详细分析红黑树

    目录 红黑树 红黑树的概念 红黑树的性质 红黑树结点的定义 红黑树的插入操作 情况一 情况二 情况三 红黑树的验证 用红黑树封装map.set 红黑树的迭代器 封装map 封装set 红黑树 红黑树的概念 红黑树的概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black. 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的. 红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是

  • C++ 超详细分析数据结构中的时间复杂度

    别别着急划走哈,如果你跟我一样是大学生,那么你发现了一个宝藏!我们往后看--> 要想了解时间复杂度和空间复杂度,我们得知道什么是时间复杂度和空间复杂度! 有的人看到这就明白了,而有的人却去追求它的内涵: 见名知意嘛,时间复杂度不就是表示一个算法运行完所需要的时间?这还用问?错错错! 我来举一个很简单的例子:你家隔壁老王买了一台 i9 12900k 和 RTX3080Ti 整个64GB的内存,你眼瞅着你 4G的内存,洋垃圾的处理器,打开个PS都要冒烟的那种,来来来,你跟我说说能比吗? 所以简单来说

  • Java数据结构超详细分析二叉搜索树

    目录 1.搜索树的概念 2.二叉搜索树的简单实现 2.1查找 2.2插入 2.3删除 2.4修改 3.二叉搜索树的性能 1.搜索树的概念 二叉搜索树是一种特殊的二叉树,又称二叉查找树,二叉排序树,它有几个特点: 如果左子树存在,则左子树每个结点的值均小于根结点的值. 如果右子树存在,则右子树每个结点的值均大于根结点的值. 中序遍历二叉搜索树,得到的序列是依次递增的. 二叉搜索树的左右子树均为二叉搜索树. 二叉搜索树的结点的值不能发生重复. 2.二叉搜索树的简单实现 我们来简单实现以下搜索树,就不

  • Java 栈与队列超详细分析讲解

    目录 一.栈(Stack) 1.什么是栈? 2.栈的常见方法 3.自己实现一个栈(底层用一个数组实现) 二.队列(Queue) 1.什么是队列? 2.队列的常见方法 3.队列的实现(单链表实现) 4.循环队列 一.栈(Stack) 1.什么是栈? 栈其实就是一种数据结构 - 先进后出(先入栈的数据后出来,最先入栈的数据会被压入栈底) 什么是java虚拟机栈? java虚拟机栈只是JVM当中的一块内存,该内存一般用来存放 例如:局部变量当调用函数时,我们会为函数开辟一块内存,叫做 栈帧,在 jav

  • C++ 超详细分析多态的原理与实现

    目录 多态的定义及实现 多态的构成条件 虚函数重写 C++11的override和final 抽象类 多态的原理 虚函数表 动态绑定与静态绑定 单继承和多继承关系的虚函数表 单继承中的虚函数表 多继承中的虚函数表 常见问题 多态的定义及实现 多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态. 比如买票这个行为,当普通人买票时,是全价买票:学生买票时,是半价买票:军人买票时是优先买票. 多态的构成条件 多态是在不同继承关系的类对象,去调用同一函数

  • C++ 超详细分析数据结构中的时间复杂度

    目录 什么是时间复杂度和空间复杂度 如何计算时间复杂度和空间复杂度 如何计算时间复杂度和空间复杂度 别别着急划走哈,如果你跟我一样是大学生,那么你发现了一个宝藏!我们往后看--> 什么是时间复杂度和空间复杂度 要想了解时间复杂度和空间复杂度,我们得知道什么是时间复杂度和空间复杂度! 有的人看到这就明白了,而有的人却去追求它的内涵: 见名知意嘛,时间复杂度不就是表示一个算法运行完所需要的时间?这还用问?错错错! 我来举一个很简单的例子:你家隔壁老王买了一台 i9 12900k 和 RTX3080T

随机推荐