上一篇文章总结了 TypeScript的类型注解,这一篇来聊聊同样重要的函数和类

函数

以下声明了一个函数类型,通过type来定义类型别名,void 表示没有返回值

type fnType = () => void;
作为参数

函数可以作为参数,传递到另一个函数中

type fnType = () => void;
function foo(fn: fnType) {
  fn();
}
function bar() {
  console.log("bar");
}
foo(bar); // bar

与js代码不同的点在于传入的参数需要定义类型注解

定义函数

定义函数的时候,需要给入参指定类型注解,返回值如果可以自行推导出来,也可以不写,如 add 和 minus 函数,但是作为参数时,是必须要写的,如 calc 函数中的入参 fn

function add(num1: number, num2: number): number {
  return num1 + num2;
}
function minus(num1: number, num2: number): number {
  return num1 - num2;
}
function calc(
  num1: number,
  num2: number,
  fn: (num1: number, num2: number) => void
) {
  console.log(fn(num1, num2));
} calc(30, 20, add); // 50
calc(30, 20, minus); // 10
函数参数的类型

ts中函数会规定参数的类型和个数,当个数不确定时,可以使用可选类型、剩余参数、默认值

可选类型

可选类型相当于该定义的类型和undefined的联合类型,所以参数有三种选择、传入该类型、不传或者undefined

function foo(x: number, y?: number) {
  console.log(x, y);
}
foo(1, 2); // 1 2
foo(3); // 3 undefined
foo(4, undefined); // 4 undefined

参数默认值

参数设置了默认值就使之称为了可选类型,不过有默认值的参数最好放在必传参数后面

function baz(x: number = 20, y: number) {
  console.log(x, y);
}
baz(10, 20); // 10 20
baz(undefined, 20); // 20 20

剩余参数

剩余参数要放在必传参数之后

function sum(num: number, ...args: number[]) {
  console.log(num, args);
}
sum(10); // 10 []
sum(10, 20); // 10 [20]
sum(10, 20, 30); // 10 [20, 30]
this的默认推导

在对象的方法中定义的this,ts是可以自动推导的,但是独立函数中的this,是推导不出来的。

必须要在独立函数中定义this的类型

type ThisType = { name: string };
const eating = function (this: ThisType) {
  console.log(this.name + " eating~");
}; const person = {
  name: "kiki",
  eating,
};
person.eating()
函数重载

函数重载指的是函数名相同,参数个数或者类型不同,所定义的多个处理方法。

比如需要对字符串拼接或者数字求和操作,虽然我们知道 + 号可以用在字符串和数字上,但是在类型检测严格的ts代码中,这样写编译是不通过的,需要使用【类型缩小】,缩小类型的判断,再进行处理。

通过联合类型,参数组合的可能性越多,需要越多的if语句来进行判断,并且函数的返回值类型也是未知的,在这种情况下可以使用【函数重载】

在ts中,定义函数名和参数类型的重载函数,再通过实现函数来定义具体实现。 会根据数据类型在重载函数中调用,再执行实现函数的代码。

function add(x: number, y: number): number;
function add(x: string, y: string): string; function add(x: any, y: any) {
  return x + y;
}
console.log(add(1, 2));
console.log(add("a", "b"));

如果传递的参数与重载函数中定义参数不同,是无法通过编译的。

初始化

类中定义的变量需要初始化,可以在定义类的时候就赋值或者通过constructor来操作

class Person {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
const person = new Person("alice", 20);
继承

ts和js中一致,都是通过 extends 实现继承,使用父级参数和方法时使用 super 关键字。

class Person {
  name: string;
  age: number;
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
} class Student extends Person {
  sno: number;
  constructor(name: string, age: number, sno: number) {
    super(name, age);
    this.sno = sno;
  }
}
const student = new Student("alice", 20, 1);
多态

使用多态可以写出更加具备通用性的代码,如果想要实现多态首先得有继承,并且父类引用指向子类对象。

class Animal {
  action() {
    console.log("animal action");
  }
}
class Dog extends Animal {
  action() {
    console.log("dog running");
  }
}
class Fish extends Animal {
  action() {
    console.log("fish swimming");
  }
}
function doAction(animals: Animal[]) {
  animals.forEach((animal) => {
    animal.action();
  });
}
doAction([new Dog()]);
doAction([new Dog(), new Fish()]);
doAction([new Dog(), new Fish(), new Animal()]);

这里相当于 const animal1: Animal = new Dog() ,看起来是 Animal 对象,其实是 Dog 对象,这里的父类引用指向的是子类对象,所以最后执行的是 Dog 对象的方法

成员修饰符

成员修饰符有以下三种

  • public 表示共有的,任何地方都可见,当没有定义成员修饰符时,默认为public
  • private 私有的,只有类中能够访问到
  • protected 被保护的,表示类自身和子类可以访问到

public

class Person {
  public username: string = "alice";
  getName() {
    return this.username;
  }
}
const person = new Person();
console.log(person.username);

private

通过private修饰的变量,在实例对象上也是不可访问的。

protected

通过protected修饰的变量,在实例对象上也是不可访问的。

readonly

readonly表示该属性只读,初始化了之后就不能以任何方式修改,无论是在类内部,还是修改实例对象,但当它是一个对象时,只要不改变它的内存地址,修改对象的属性是可以的。

访问器

访问器给私有属性提供get/set方法,让我们在类外部获取/修改私有属性

class Person {
  private _name: string;
  constructor(newName: string) {
    this._name = newName;
  }
  get name() {
    return this._name;
  }
  set name(newName) {
    if (newName) this._name = newName;
  }
} const person = new Person("alice");
console.log(person.name);
person.name = "kiki";
console.log(person.name);
person.name = "";
console.log(person.name);

通过get/set属性来修改私有属性可以做到拦截/判断的作用

静态成员

静态成员通过 static 关键字来定义,通过 static 定义的属性,是定义在类自身的,只能通过自己访问,在类内部和实例对象都是无法访问到的。

抽象类

在定义很多通用的调用接口时,我们通常会让调用者传入父类,通过多态来实现更加灵活的调用方式。

但是,父类本身可能并不需要对某些方法进行具体的实现,所以父类中定义的方法, 我们可以定义为抽象方法。

abstract class Shape {
  abstract getArea(): void;
} class Circle extends Shape {
  private radius: number;
  constructor(radius: number) {
    super();
    this.radius = radius;
  }
  getArea() {
    return this.radius * this.radius * 3.14;
  }
} class Rectangle extends Shape {
  private width: number;
  private height: number;
  constructor(width: number, height: number) {
    super();
    this.width = width;
    this.height = height;
  }
  getArea() {
    return this.width * this.height;
  }
} function calcArea(shape: Shape) {
  return shape.getArea();
} console.log(calcArea(new Circle(3)));
console.log(calcArea(new Rectangle(2, 6)));

抽象方法和方法通过 abstract 来修饰,并且抽象类定义时有两条规则:

  • 抽象方法必须要在子类实现
  • 抽象类是不能被实例化的

类的类型

类本身也是可以作为一种数据类型的,可以用作类型注解。

class Person {
  name: string = "alice";
  eating() {}
}
const person: Person = {
  name: "kiki",
  eating() {
    console.log("i am eating");
  },
};
function printPerson(person: Person) {
  console.log(person.name);
} printPerson(new Person());
printPerson(person);
printPerson({ name: "macsu", eating() {} });

只要符合类的格式,就可以使用类的类型

函数和类在JS和TS中都是至关重要的,可以帮助开发者更好规范开发时的代码,减少线上故障~

以上就是关于TypeScript函数和类的内容,关于js和ts,还有很多需要开发者掌握的地方,可以看看我写的其他博文,持续更新中~

可不要忽视了TypeScript中函数和类的重要性的更多相关文章

  1. TypeScript 中函数的理解?与 JavaScript 函数的区别?

    一.是什么 函数是JavaScript 应用程序的基础,帮助我们实现抽象层.模拟类.信息隐藏和模块 在TypeScript 里,虽然已经支持类.命名空间和模块,但函数仍然是主要定义行为的方式,Type ...

  2. Python中函数、类、模块和包的调用

    初学python阶段,大多数人对函数.类.模块和包的调用都搞得不是很清楚,这篇随笔就简单的进行说明. (1)函数 当函数定义好之后,可以直接调用. 比如:def summ(add1,add2),那么 ...

  3. python中不同文件中函数和类的调用

    最近在学习Python的时候,遇到了一个不同文件中类无法调用的问题,搜了很多,发现很多人针对 这个问题都说的相当含糊,让我费了好大劲才把这个东东搞明白.记录一下,权且温习. 调用分两种,一种是同种文件 ...

  4. JavaScript中函数和类(以及this的使用<重点>,以及js和jquery讲解,原生js实现jquery)

    1.javascript中以函数来表示类: 一般函数是小写开头:function foo() 类开头是大写:function Foo() 实例化类: obj = new Foo() 其他属性就同类是一 ...

  5. Typescript 回调函数、事件侦听的类型定义与注释--拾人牙慧

    实际项目中会运到的 Typescript 回调函数.事件侦听的类型定义,如果刚碰到会一脸蒙真的,我就是 这是第一次我自己对 Typescript 记录学习,所以得先说一下我与 Typescript 的 ...

  6. 如何在JavaScript中手动创建类数组对象

    前言 关于什么是js的类数组对象这里不再赘述.可以参考这个链接,还有这里. js中类数组对象很多,概念简单的讲就是看上去像数组,又不是数组,可以使用数字下标方式访问又没有数组方法. 例: argume ...

  7. typeScript中的函数

    // 函数的定义 //es5定义函数的方法 /* //函数声明法 function run(){ return 'run'; } //匿名函数 var run2=function(){ return ...

  8. TypeScript 之 函数

    https://m.runoob.com/manual/gitbook/TypeScript/_book/doc/handbook/Functions.html 为函数定义类型 为函数添加类型: fu ...

  9. TypeScript Function(函数)

    在JavaScript中,函数是构成任何应用程序的基础块.通过函数,你得以实现建立抽象层.模仿类.信息隐藏和模块化.在TypeScript中,虽然已经存在类和模块化,但是函数依旧在如何去"处 ...

  10. TypeScript入门-函数

    ▓▓▓▓▓▓ 大致介绍 TypeScript为JavaScript函数添加了额外的功能,让我们可以更容易地使用.TypeScript中的函数也包括JavaScript中最常见的两种函数 functio ...

随机推荐

  1. 粘包/拆包问题一直都存在,只是到TCP就拆不动了。

    OSI open-system-Interconnection TCP/IP 5层协议栈 应用层和操作系统的边界是 系统调用 ,对应到网络编程是socket api TCP/UDP 概况 TCP粘包问 ...

  2. 【python爬虫】对于微博用户发表文章内容和评论的爬取

    此博客仅作为交流学习 对于喜爱的微博用户文章内容进行爬取 (此部分在于app页面进行爬取,比较方便) 分析页面 在这里进行json方法进行,点击Network进行抓包 发现数据加载是由这个页面发出的, ...

  3. stl------iterator迭代器与Vector

    Vector不定长数组 例:http://newoj.acmclub.cn/contests/1258/problem/4 1926: 2018蓝桥杯培训-STL应用专题-day 2 vector作业 ...

  4. 2022-01-03:比如arr = {3,1,2,4}, 下标对应是:0 1 2 3, 你最开始选择一个下标进行操作,一旦最开始确定了是哪个下标,以后都只能在这个下标上进行操作。 比如你选定1下标,

    2022-01-03:比如arr = {3,1,2,4}, 下标对应是:0 1 2 3, 你最开始选择一个下标进行操作,一旦最开始确定了是哪个下标,以后都只能在这个下标上进行操作. 比如你选定1下标, ...

  5. gitlab-runner 中的 Docker-in-Docker

    笔者个人理解:gitlab-runner 安装后就是一个监听状态的 runner,而通过 gitlab-runner register 注册的"实例"其实只是预定义的配置节,当消息 ...

  6. 理论+实操,带你了解多沙箱容器运行时Kuasar

    摘要:华为云DTSE技术布道师张天阳结合沙箱容器发展历程,介绍华为云多沙箱容器运行时 Kuasar 项目优势,开启多沙箱容器运行时上手实践体验. 本文分享自华为云社区<理论+实操,带你了解多沙箱 ...

  7. 这可能是最全面的Java面试八股文了

    Java的特点 Java是一门面向对象的编程语言.面向对象和面向过程的区别参考下一个问题. Java具有平台独立性和移植性. Java有一句口号:Write once, run anywhere,一次 ...

  8. linux 账户和权限

    目录 一.用户账户管理 二.组账号管理 三.用户账户文件和组账户文件 四.查询账户命令 五.设置目录与文件权限 六.设置命令与文件归属 七.默认文件属性umask 八.修改主机名 一.用户账户管理 u ...

  9. SpringBoot配置文件加载

    Spring Boot 配置文件加载是通过 Spring Boot 的自动配置机制实现的,它可以根据不同的环境加载不同的配置文件,包括 application.properties.applicati ...

  10. 你以为搞个流水线每天跑,团队就在使用CI/CD实践了?

    在实践中,很多团队对于DevOps 流水线没有很透彻的理解,要不就创建一大堆流水线,要不就一个流水线通吃.实际上,流水线的设计和写代码一样,需要基于"业务场景"进行一定的设计编排, ...