JavaScript | 青训营

90 阅读7分钟

JavaScript编码的原则

avaScript编码的原则可以总结为各司其责、组件封装和过程抽象。

image.png

  1. 各司其责:在JavaScript编码中,应该尽量避免不必要地使用 JavaScript 直接操作样式。通过使用 CSS 类来表示状态和样式,可以更好地分离结构(HTML)和样式(CSS)的责任。

  2. 组件封装:是指将相关的功能和界面封装成可复用的组件,以提高代码的重用性和可维护性。在JavaScript中,可以使用对象、类或者模块来实现组件封装。

  3. 过程抽象:是指将复杂的操作封装成简单的步骤或者函数,以提高代码的可读性和可维护性。在JavaScript中,可以使用函数来实现过程抽象。

TypeScript

TypeScript是一种开源的编程语言,它是JavaScript的超集。它添加了静态类型和更强大的面向对象编程特性,可以在编译时进行类型检查,提高代码的可靠性和可维护性。

TypeScript与JavaScript的区别

  1. 类型系统:JavaScript是一种动态弱类型语言,变量可以被赋值为不同类型的值。而TypeScript是一种静态强类型语言,变量在声明时需要指定类型,并且不允许改变变量的数据类型。
  2. 工具链支持:TypeScript提供了完善的工具链,包括类型检查、代码提示和自动补全等功能,能够提高开发效率和代码质量。
  3. 下一代JS特性支持:TypeScript支持ECMAScript的最新特性,并且可以在编译时将其转换为可在不同浏览器中运行的JavaScript代码。

image.png

TypeScript基础

基础类型

  1. boolean:布尔类型,表示真或假。

  2. number:数值类型,表示数字。

  3. string:字符串类型,表示文本。

  4. undefined:未定义类型,表示变量未被赋值。

  5. null:空类型,表示变量的值为空。

  6. any:任意类型,表示可以是任何类型的值。在TypeScript中,如果不指定变量的类型,默认为any类型。

  7. unknown:未知类型,类似于any类型,但使用unknown类型时需要进行类型检查或类型断言才能进行具体操作。

  8. void:空类型,表示没有任何返回值的函数。

  9. never:永远不存在的类型,表示函数永远不会返回或抛出异常。

  10. 数组类型[]:表示一个由某种类型元素组成的数组,例如number[]表示由数字组成的数组。

  11. 元组类型tuple:表示一个已知长度和类型的数组,每个元素可以是不同的类型。

函数类型

  1. 定义:在TypeScript中定义函数类型时,需要指定输入参数的类型和输出类型。可以使用箭头函数或函数声明来定义函数类型。

  2. 输入参数:函数的输入参数可以指定类型,并且支持可选参数和默认参数。

  3. 输出参数:函数的输出参数可以根据返回语句自动推断类型,默认情况下如果没有明确指定返回类型,则推断为void类型。

  4. 函数重载:在TypeScript中可以定义函数重载,即同名函数但参数类型或数量不同。通过函数重载可以实现对不同输入类型的处理。

Interface

定义:接口是用于定义对象类型的工具

特点:

  1. 可选属性(Optional Properties):使用问号(?)表示属性是可选的,即可以存在也可以不存在。
interface Person {
  name: string;
  age?: number; // 可选属性
}

let person1: Person = { name: "Alice" };
let person2: Person = { name: "Bob", age: 25 };
  1. 只读属性(Readonly Properties):使用readonly关键字表示属性只能在创建对象时赋值,后续不能修改。
interface Point {
  readonly x: number; // 只读属性
  readonly y: number;
}

let p: Point = { x: 10, y: 20 };
// p.x = 5; // 编译错误,无法修改只读属性
  1. 函数类型(Function Types):接口不仅可以描述对象类型,还可以描述函数类型。使用箭头函数或函数声明来定义函数类型,并指定参数类型和返回类型。
interface Greeter {
  (name: string): string; // 函数类型
}

let greet: Greeter = function (name: string) {
  return `Hello, ${name}!`;
};

console.log(greet("Alice")); // Hello, Alice!
  1. 自定义属性:接口可以包含除了已知属性之外的其他属性,这样可以灵活地添加自定义属性。
interface Book {
  title: string;
  author: string;
  [propName: string]: any; // 允许添加其他属性
}

let book: Book = { title: "TypeScript in Action", author: "Jane Smith", year: 2023 };

接口的灵活性使得它可以更好地适应各种场景,实现duck typing(鸭子类型)的特性。

Class类

定义:类的定义和JavaScript相似,同时也增加了一些额外的特性

特点:

  1. 修饰符(Modifiers):类中的属性和方法可以使用public、private、protected修饰符来控制访问权限。
  • public:默认修饰符,表示属性或方法可以在类的内部和外部访问。
  • private:表示属性或方法只能在类的内部访问。
  • protected:表示属性或方法可以在类的内部和子类中访问。
class Person {
  public name: string;
  private age: number;
  protected gender: string;

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

  public sayHello() {
    console.log(`Hello, my name is ${this.name}.`);
  }
}

let person = new Person("Alice", 25, "female");
console.log(person.name); // 可以访问 public 属性
// console.log(person.age); // 编译错误,无法访问 private 属性
// console.log(person.gender); // 编译错误,无法访问 protected 属性
person.sayHello(); // Hello, my name is Alice.
  1. 抽象类(Abstract Classes):抽象类是不能被实例化的类,只能作为其他类的基类。它可以包含抽象方法,子类必须实现这些抽象方法。
abstract class Animal {
  abstract makeSound(): void; // 抽象方法

  move(): void {
    console.log("Moving...");
  }
}

class Dog extends Animal {
  makeSound(): void {
    console.log("Woof!");
  }
}

let dog = new Dog();
dog.makeSound(); // Woof!
dog.move(); // Moving...
  1. 接口约束类(Interfaces with Classes):接口可以用于约束类,使用implements关键字实现接口中定义的方法。
interface Vehicle {
  start(): void;
  stop(): void;
}

class Car implements Vehicle {
  start(): void {
    console.log("Car started.");
  }

  stop(): void {
    console.log("Car stopped.");
  }
}

let car = new Car();
car.start(); // Car started.
car.stop(); // Car stopped.

TypeScript进阶

高级类型

  1. 联合类型(Union Types):使用|符号表示,表示一个值可以是多个类型之一。
let age: number | string; // age 可以是 number 类型或 string 类型
age = 25;
age = "25";
  1. 交叉类型(Intersection Types):使用&符号表示,表示一个值拥有多个类型的特性。
interface Person {
  name: string;
  age: number;
}

interface Employee {
  company: string;
  position: string;
}

type EmployeePerson = Person & Employee; // EmployeePerson 拥有 Person 和 Employee 的特性

let employee: EmployeePerson = {
  name: "Alice",
  age: 25,
  company: "ABC Inc.",
  position: "Engineer"
};
  1. 类型断言(Type Assertion):用来告诉编译器某个值的具体类型。有两种写法:尖括号写法和as写法。
let value: any = "Hello, TypeScript!";
let length1: number = (<string>value).length; // 使用尖括号写法
let length2: number = (value as string).length; // 使用as写法
  1. 类型别名(Type Alias)和接口(Interface):
  • 定义:类型别名用来给类型起一个新名字,可以用于任何类型,包括基本类型、联合类型、交叉类型等;接口用来描述对象的形状。

  • 相同点:

    • 都可以用来定义对象类型或函数类型。
    • 都允许继承,使用extends关键字。
  • 差异点:

    • 类型别名可以定义基本类型,而接口不能。
    • 类型别名可以合并多个重复声明的别名,而接口不能。
type Age = number; // 类型别名给 number 类型起一个新名字
let age1: Age = 25;

interface Person {
  name: string;
}

interface Person {
  age: number;
}

let person: Person = {
  name: "Alice",
  age: 25
};

泛型

基本定义:

  1. 泛型的语法是在尖括号<>中写入类型参数,通常用T表示。

  2. 在使用泛型时,可以通过两种方式指定类型:

    1. 显式指定要使用的类型。
    2. 通过TS类型推断,让编译器自动推导类型。
  3. 泛型的作用是临时占位,后续根据传入的类型进行推导。

泛型工具类型: 在TypeScript中,还有一些内置的泛型工具类型,可以帮助我们处理常见的类型操作。

  • Partial:将类型的所有属性变为可选。

  • Required:将类型的所有属性变为必选。

  • Readonly:将类型的所有属性变为只读。

基础操作符: 除了泛型工具类型,TypeScript还提供了一些基础的操作符来处理类型。

  • typeof:用于获取类型的字符串表示。

  • keyof:用于获取类型的所有键(属性名)组成的联合类型。

  • in:用于遍历枚举类型。

  • T[K]:用于索引访问,可以通过类型T和键K获取对应的属性值的类型。

  • extends:用于泛型约束,限制泛型类型必须是某个类型的子类型。

TypeScript实战

声明文件

  1. declare:当我们使用第三方库或模块时,如果该库没有提供类型声明文件,我们可以使用declare关键字来创建一个声明文件。声明文件的扩展名通常是.d.ts,并包含对库或模块的类型定义。可以将这些声明文件放在项目中的一个单独目录中,或者使用@types包来获取类型声明。

  2. @types:是一种特殊的npm包,用于提供第三方库的类型声明。当我们在使用第三方库时,如果该库本身没有提供类型声明文件,我们可以通过安装对应的@types包来获取类型声明。

  3. tsconfig.json文件是TypeScript项目的配置文件,用于指定TypeScript编译器的行为和选项。我们可以在其中定义编译目标、模块解析方式、输出目录等。

后端接口约束

在后端开发中,我们经常需要与数据库、API等交互,并与服务器进行数据传输。使用TypeScript可以对后端接口进行约束,以确保数据的正确性和类型安全性。