Nest(3):扫盲篇:TypeScript 类和装饰器

news2024/12/24 21:59:02

前言

先回顾下前文中介绍了哪些内容:

  • 使用 @nestjs/cli 创建和管理 Nest 应用
  • Hello, World 示例代码分析
  • Nest 基本概念:模块,控制器,服务
  • 常用的装饰器:@Module、@Controller、@Get、@Injectable
  • Nest 目录结构分析
  • @nest/cli 脚手架的命令

本文先不继续讲解 Nest 中的内容,而是打算介绍 TypeScript 中的两个语法:类和装饰器,帮助新手理解 Nest 中代码的写法。

如果你对 TypeScript 已经很熟悉,根据自己实际情况有选择的阅读即可。

类 Class

NestJS 支持面向对象编程,也支持函数式编程。但在实际开发中还是以面向对象为主,而面向对象又是和类紧密联系的,所以对于类的一些概念和语法,一定要熟练掌握。

ES6 推出了 class 类,本质是过去的构造函数的语法糖。TypeScript 中的类的用法和 ES 标准中的类大差不离,多了一些更加 OOP 的语法支持。、

下面是对类的一些常用语法的说明。

类成员

在 JS 中类的成员有两种,分别是成员属性成员方法。TypeScript 官方文档中会称之为字段(filed)和方法(method)。本文描述时会按照 JS 的习惯。

属性

在 TS 中声明类使用 class 关键字,如果实例有属性,则必须先声明其属性类型。如下,声明一个 Person 类:

class Person {
  // 声明类的属性,可以使用类型注解声明类型
  name: string;
  age: number;
    
  // 也可以省略,则默认是 any 类型
  address;
}

// 实例化
const person = new Person('kw', 18);
console.log(person.name);

初始值

类的属性可以设置初始值,既可以在声明时设置,也可以在构造函数中设置:

class Person {
  name: string = 'kw';
  age: number = 18;
}

class Person { 
  name: string;
  age: number;
    
  constructor() {
      this.name = 'kw';
      this.age = 18;
  }
}

--strictPropertyInitialization

这是 tsconfig.json 中的一个配置项,是否严格检查类的属性初始化操作。默认为 true,表示类的成员属性必须在声明时或者在构造函数中进行初始化操作。如果该选项开启了:

class Person { 
  // 正确,在声明时完成了初始化
  name: string = 'kw';
  // 正确,在构造函数中完成了初始化
  age: number;
  // 报错:Property 'address' has no initializer and is not definitely assigned in the constructor.
  address: string;
    
  constructor() {
      this.age = 18;
  }
}

有些场景下需要在别的地方进行属性的初始化,此时可以对属性应用非空断言:

class Person {
    name!: string;
}

此时虽然没有做初始化,但是编译器也不会报错了。

readonly 修饰符

使用 readonly 修饰类的成员属性后,该属性就变为了只读属性,只能读,不能修改。因此对于只读属性,必须在声明时,或者在构造函数中进行初始化,否则会报错。

class Person { 
  readonly name: string = 'kw';
  age: number;
}

构造方法

构造方法用于创建和初始化类的实例对象。使用 new 操作一个类时,就会触发这个类的构造方法的执行。

构造方法不是必须的,类有一个默认的空的构造方法。但是如果需要为类的实例设置不同的属性,则必须实现一个构造方法。

class Person { 
  name: string;
  age: number;

    
  constructor(name: string, age: number) {
      this.name = name;
      this.age = age;
  }
}

// 实例化
const person = new Person();
console.log(person.name);
console.log(person.age);

可访问性修饰符

修饰符用来限制类的成员属性和成员方法的可访问范围

假设有一个 A 类和继承自 A 的 B 类,则可见范围有三种:任意地方,A 类内部,B 类的内部。分别对应了三种修饰符:

  • public:默认的修饰符,表示成员是公有的,可在任意地方被访问
  • protected:受保护的修饰符,表示成员能在当前类的内部,子类的内部被访问到
  • private:私有修饰符,表示成员只能在类的内部被访问到

image-20230116230814660

class A {
    // 默认为 public,等同于 public a
    a;
    // 受保护的属性,可以在当前类和子类中访问
    protected b;
    // 私有属性,只能在当前类内部访问
    private c;
    
    test() {
        console.log(this.c)
    }
}

class B extends A {
    test() {
        console.log(this.b)
    }
}

参数属性

参数属性(Parameter Properties)是一种能简化代码的语法糖。例如要声明一个类,要先声明成员属性,再写构造方法进行初始化:

class Person { 
  name: string;
  age: number;

    
  constructor(name: string, age: number) {
      this.name = name;
      this.age = age;
  }
}

const person = new Person('kw', 18)
console.log(person.name)
console.log(person.age)

当这个类有非常多的属性时,光这些初始化代码可能就要写几十行。

在 TypeScript 中,在构造函数的参数前加上一个可见性修饰符publicprivateprotected 或者 readonly,该参数就变为了参数属性,TypeScript 会将这些构造函数的参数转换为具有相同名称和值的类属性

上面的 Person 类就等同于这种写法:

class Person { 
  // 在声明类成员时 public 修饰符可以省略,但在使用参数属性时,不能省略
  constructor(public name: string, public age: number) {
    // 不需要函数体
  }
}

const person = new Person('kw', 18)
console.log(person.name)
console.log(person.age)

回顾下 AppController 控制器的代码:

export class AppController {
  constructor(private readonly appService: AppService) {}
}

现在再看这段代码是什么含义就很清晰了,它的完整写法是:

export class AppController {
  // appService 是私有属性且只读,需要在声明或者构造函数中完成初始化
  private readonly appService: AppService;
    
  constructor(appService: AppService) {
      this.appService = appService;
  }
}

类的其他语法

类还有一些其他语法,比如访问器,静态成员属性和方法,继承等等。这些内容如果还不熟悉,可以阅读其他文章博客或者文档,我们先学习上面这些,够用为主。

装饰器 Decorator

装饰器,看名字就知道是用起装饰作用的。它用来增强类(class)的功能,许多面向对象的语言都有这种语法。

虽然装饰器的历史悠久,但是由于 JS 这门语言本身很少有场景需要使用到装饰器,所以目前装饰器仍处于提案阶段,并且这一提,就是很多年。记得最早在 ES2015 中,装饰器语法就已经处于提案阶段了。

虽然 ES 标准的装饰器还未成为标准,但是 TS 中的装饰器可以大胆使用。

装饰器的本质就是函数,只不过使用形式上和普通函数有所不同:普通函数使用 () 调用,装饰器使用 @ 调用。普通函数可以在任意位置执行,装饰器只能用在类和类的成员身上

装饰器和装饰器工厂

先来看一个装饰器的最简单的例子。

function Fn(target: any) {
  let a = new target;
  a.say()
}

@Fn
class A {
  say() {
    console.log('hello')
  }
}

代码执行,打印 “hello”。

Fn 是一个装饰器函数,当被用在类上时,它所接收的参数就是该类。所以可以在装饰器内部,实例化 A 类型,并调用 a对象 的实例方法。

如果需要一些定制化的内容,想让装饰器接收一些其他的参数,就要用到装饰器工厂了。装饰器工厂就是一个返回装饰器的工厂函数。比如:

function FnFactory(name: string) {
    
  // 返回一个装饰器
  return function(target: any) {
      let a = new target;
      a.say(name)
  }
  
}

@FnFactory('kw')
class A {
  say(name: string) {
    console.log('hello, ', name)
  }
}

代码执行,打印 “hello, kw”。

注意区分装饰器和装饰器工厂的使用,普通的装饰器的用法是 @装饰器,装饰器工厂的用法需要带上圆括号,@装饰器()

类装饰器

根据所修饰对象的不同,装饰器具体可以分为:

  • 类装饰器
  • 属性装饰器
  • 方法装饰器
  • 存取器装饰器
  • 参数装饰器

上面的示例中,装饰器在类上调用,这就是类装饰器。类装饰器只有一个参数,就是被装饰的类

属性装饰器

应用在类的属性成员上的装饰器,可接收两个参数:

  • 如果是静态属性,则是类的构造方法;如果是实例属性,则是类的原型对象
  • 属性名
function Property() {
  return function (target: any, property: any) {
    console.log(target)
    console.log(property)
  }
}

class C {
  @Property()
  name!:string;
}

// {}
// name

方法装饰器

方法装饰器用的比较多,它接收3个参数:

  • 如果修饰的是静态成员,则为类的构造函数;如果修饰的是实例方法,则为类的原型对象
  • 方法名
  • 方法的属性描述符
function enumerable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.enumerable = value;
  };
}

class Greeter {
  greeting: string;
    
  constructor(message: string) {
    this.greeting = message;
  }
 
  @enumerable(false)
  greet() {
    return "Hello, " + this.greeting;
  }
}

这个装饰器的作用是修改属性描述符的 enumerable 属性,设为 false,也就代表着该方法不能被枚举了。

存取器装饰器

用在访问器身上的装饰器,可接收参数和方法装饰器相同。

function configurable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.configurable = value;
  };
}

class Point {
  private _x: number;
  private _y: number;
  constructor(x: number, y: number) {
    this._x = x;
    this._y = y;
  }
 
  @configurable(false)
  get x() {
    return this._x;
  }
 
  @configurable(false)
  get y() {
    return this._y;
  }
}

该装饰器可以动态修改存取器的属性描述符的 configurable 属性,示例中将其设置为 false,表示不能被删除,也不能被修改。

参数装饰器

应用在构造方法或者实例方法的参数上的装饰器,它接收三个参数:

  • 如果修饰的是静态成员方法,则为类的构造函数;如果修饰的是构造方法或者实例方法,则为类的原型对象
  • 参数所在方法的名字
  • 参数在原函数的参数列表中的位置,也就是第几个参数
// 参数装饰器
function Param() {
  return function(target: any, param: string, index: number) {
    console.log(target)
    console.log(param)
    console.log(index)
  }
}

class Person {
  say(@Param() msg:string) {
    console.log(msg)
  }
}

// {}
// say
// 0

@Controller 和 @Get 装饰器

看了这么多装饰器,已经眼花缭乱了,来看一个实际的例子。

打开 app.controller.ts,修改为以下内容:

import { Controller, Get, Query } from '@nestjs/common';
import { AppService } from './app.service';

// 声明类为控制器,并为该模块的路由设置一个请求前缀 news
@Controller('news')
export class AppController {
  constructor(private readonly appService: AppService) {}

  // 和路由前缀拼接,处理 Get /news/list 请求
  @Get('list')
  // 参数装饰器,入参是请求中的 query 参数
  getHello(@Query('page') page) {
    return {
      code: 0,
      data: {
        list: [],
        page,
      },
    };
  }
}

打开浏览器,访问 localhost:3000/news/list?page=10

image-20230117011356232

当然,Nest 中还有非常多的装饰器,后面也会继续介绍。

总结

本文没有继续介绍 NestJS 有关的内容,而是讲解了 TypeScript 中两个很重要的语法:类和装饰器,可以帮助新手理解 Nest 应用中的一些代码写法的含义。

毕竟不积跬步,无以至千里,打好这些基础,后面上手的也会更顺利。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/911536.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

撸卡、撸货、采退成功率低是什么原因,以及解决办法

在亚马逊和沃尔玛平台上,无论是进行测评、撸卡还是撸货,首要任务就是确保环境的安全性和稳定性。稳定的环境是进行测评和撸卡的基础,如果无法解决安全性问题,那么就不值得进行这些项目。进行环境技术研发已有六七年的时间&#xf…

GB28181国标平台测试软件NTV-GBC(包含服务器和模拟客户端)

GB28181国标平台测试软件NTV-GBC用于对GB28181国标平台进行测试(测试用例需要服务器软件,服务器软件可以是任何标准的国标平台,我们测试使用的是NTV-GBS),软件实现了设备注册、注销、目录查询,消息订阅、INVITE&#x…

惊艳亮相!天翼物联5G工业物联产品获主流媒体关注

近日,由工业和信息化部与广东省人民政府共同主办的2023年中国数字经济创新发展大会举行。作为赋能汕头玩具创意和纺织服装“两特”产业数字化的重要抓手,天翼物联5G工业物联产品惊艳亮相中国电信主展台,并得到了广东广播电视台等媒体的关注。…

亚马逊买家怎么留评

亚马逊买家可以按照以下步骤在购买后留下产品评价: 1、登录亚马逊账户:首先,在网页浏览器中打开亚马逊网站,登录你的亚马逊账户。 2、找到订单:在页面上找到并点击你购买过的商品的"我的订单"或"订单…

【LeetCode】151. 反转字符串中的单词 - 双指针

目录标题 2023-8-22 09:53:10原始优化 151. 反转字符串中的单词 2023-8-22 09:53:10 也是想到了快慢指针的思想。 原始 class Solution {public String reverseWords(String s) {int length s.length();Integer pre null;Integer last null;StringBuilder stringBuilde…

LeetCode--HOT100题(38)

目录 题目描述:226. 翻转二叉树(简单)题目接口解题思路代码 PS: 题目描述:226. 翻转二叉树(简单) 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。 LeetCode做题链…

Power apps:做个简单的扫码应用

Power apps的扫码应用只能客户端使用 一、创建一个窗口"扫码APP”,插入媒体工具“条形码读卡器” 二、如果需要在扫码时做一个动作,可以设置它的属性,比如跳转窗口之类的 三、添加一个文本标签,实现在扫码后标签显示条形码&#xff08…

(三)行为模式:5、中介者模式(Mediator Pattern)(C++示例)

目录 1、中介者模式(Mediator Pattern)含义 2、中介者模式的UML图学习 3、中介者模式的应用场景 4、中介者模式的优缺点 (1)优点 (2)缺点 5、C实现中介者模式的实例 1、中介者模式(Media…

数字图像处理—— Lab、YCbCr、HSV、RGB之间互转

Lab “Lab” 图像格式通常指的是 CIELAB 色彩空间,也称为 Lab 色彩空间。它是一种用于描述人类视觉感知的颜色的设备无关色彩空间,与常见的 RGB 和 CMYK 色彩空间不同。CIELAB 由国际照明委员会(CIE)于1976年定义,用于…

工业生产全面感知!工业感知云来了

面向工业企业数字化转型需求,天翼物联基于感知云平台创新能力和5G工业物联数采能力,为客户提供工业感知云服务,包括工业泛协议接入、感知云工业超轻数采平台、工业感知数据治理、工业数据看板四大服务,构建工业感知神经系统新型数…

MySQL分页查询-性能优化

MySQL分页查询优化 一、背景二、原因三、原理分析 https://blog.csdn.net/hollis_chuang/article/details/130570281 一、背景 业务背景:给C端10万级别的用户,同时发送活动消息,活动消息分为6类。数据背景:mysql表有百万级别的数…

学习微服务必推荐的天花板级别微服务架构笔记

有没有一本讲微服务架构比较不错的书? 貌似关于架构设计的书没有人推荐?有没有人推荐一本,最好是有关架构演变的也有相关介绍 小编就在这里推荐一份Chris Richardson写的《微服务架构设计模式》,他本人是微服务领域的专家&#xf…

常见前端面试之VUE面试题汇总二

4. slot 是什么?有什么作用?原理是什么? slot 又名插槽,是 Vue 的内容分发机制,组件内部的模板引擎使用 slot 元素作为承载分发内容的出口。插槽 slot 是子组件的一个模板 标签元素,而这一个标签元素是否显…

【Linux】网络层协议:IP

一片赤胆平乱世,手中长枪定江山 文章目录 一、IP和TCP之间的关系(提供策略 和 提供能力)二、IP报头的理解(再次理解面向数据报)三、网段划分1.为什么要进行网段划分?(方便定位目标主机&#xff…

21 移动测试神器:带你玩转Appium

Appium特点 Appium 作为目前主流的移动应用自动化测试框架,具有极强的灵活性,主要体现在以下 5 个方面: 测试用例的实现支持多种编程语言,比如 Java、Ruby、Python 等; Appium Server 支持多平台,既…

ChatGPT帮助提升工作效率和质量:完成时间下降40%,质量评分上升 18%

自ChatGPT去年11月发布以来,人们就开始使用它来协助工作,热心的用户利用它帮助撰写各种内容,从宣传材料到沟通话术再到调研报告。 两名MIT经济学研究生近日在《科学》杂志上发表的一项新研究表明,ChatGPT可能有助于减少员工之…

【三维重建】【深度学习】NeuS代码Pytorch实现--测试阶段代码解析(上)

【三维重建】【深度学习】NeuS代码Pytorch实现–测试阶段代码解析(上) 论文提出了一种新颖的神经表面重建方法,称为NeuS,用于从2D图像输入以高保真度重建对象和场景。在NeuS中建议将曲面表示为有符号距离函数(SDF)的零级集,并开发一种新的体绘…

前馈神经网络解密:深入理解人工智能的基石

目录 一、前馈神经网络概述什么是前馈神经网络前馈神经网络的工作原理应用场景及优缺点 二、前馈神经网络的基本结构输入层、隐藏层和输出层激活函数的选择与作用网络权重和偏置 三、前馈神经网络的训练方法损失函数与优化算法反向传播算法详解避免过拟合的策略 四、使用Python…

SVM详解

公式太多了,就用图片用笔记呈现,SVM虽然算法本质一目了然,但其中用到的数学推导还是挺多的,其中拉格朗日约束关于α>0这块证明我看了很长时间,到底是因为悟性不够。对偶问题也是,用了一个简单的例子才明…

超级计算机

超级计算机是一种高性能计算机,它能够以极高的速度执行大规模的计算任务。超级计算机通常由数千个甚至数百万个处理器组成,这些处理器能够同时处理大量的数据,从而实现高效的计算。超级计算机广泛应用于科学、工程、金融、天气预报等领域&…