JavaScript编码的原则
avaScript编码的原则可以总结为各司其责、组件封装和过程抽象。
-
各司其责:在JavaScript编码中,应该尽量避免不必要地使用 JavaScript 直接操作样式。通过使用 CSS 类来表示状态和样式,可以更好地分离结构(HTML)和样式(CSS)的责任。
-
组件封装:是指将相关的功能和界面封装成可复用的组件,以提高代码的重用性和可维护性。在JavaScript中,可以使用对象、类或者模块来实现组件封装。
-
过程抽象:是指将复杂的操作封装成简单的步骤或者函数,以提高代码的可读性和可维护性。在JavaScript中,可以使用函数来实现过程抽象。
TypeScript
TypeScript是一种开源的编程语言,它是JavaScript的超集。它添加了静态类型和更强大的面向对象编程特性,可以在编译时进行类型检查,提高代码的可靠性和可维护性。
TypeScript与JavaScript的区别
- 类型系统:JavaScript是一种动态弱类型语言,变量可以被赋值为不同类型的值。而TypeScript是一种静态强类型语言,变量在声明时需要指定类型,并且不允许改变变量的数据类型。
- 工具链支持:TypeScript提供了完善的工具链,包括类型检查、代码提示和自动补全等功能,能够提高开发效率和代码质量。
- 下一代JS特性支持:TypeScript支持ECMAScript的最新特性,并且可以在编译时将其转换为可在不同浏览器中运行的JavaScript代码。
TypeScript基础
基础类型
-
boolean:布尔类型,表示真或假。
-
number:数值类型,表示数字。
-
string:字符串类型,表示文本。
-
undefined:未定义类型,表示变量未被赋值。
-
null:空类型,表示变量的值为空。
-
any:任意类型,表示可以是任何类型的值。在TypeScript中,如果不指定变量的类型,默认为any类型。
-
unknown:未知类型,类似于any类型,但使用unknown类型时需要进行类型检查或类型断言才能进行具体操作。
-
void:空类型,表示没有任何返回值的函数。
-
never:永远不存在的类型,表示函数永远不会返回或抛出异常。
-
数组类型[]:表示一个由某种类型元素组成的数组,例如number[]表示由数字组成的数组。
-
元组类型tuple:表示一个已知长度和类型的数组,每个元素可以是不同的类型。
函数类型
-
定义:在TypeScript中定义函数类型时,需要指定输入参数的类型和输出类型。可以使用箭头函数或函数声明来定义函数类型。
-
输入参数:函数的输入参数可以指定类型,并且支持可选参数和默认参数。
-
输出参数:函数的输出参数可以根据返回语句自动推断类型,默认情况下如果没有明确指定返回类型,则推断为void类型。
-
函数重载:在TypeScript中可以定义函数重载,即同名函数但参数类型或数量不同。通过函数重载可以实现对不同输入类型的处理。
Interface
定义:接口是用于定义对象类型的工具
特点:
- 可选属性(Optional Properties):使用问号(?)表示属性是可选的,即可以存在也可以不存在。
interface Person {
name: string;
age?: number; // 可选属性
}
let person1: Person = { name: "Alice" };
let person2: Person = { name: "Bob", age: 25 };
- 只读属性(Readonly Properties):使用readonly关键字表示属性只能在创建对象时赋值,后续不能修改。
interface Point {
readonly x: number; // 只读属性
readonly y: number;
}
let p: Point = { x: 10, y: 20 };
// p.x = 5; // 编译错误,无法修改只读属性
- 函数类型(Function Types):接口不仅可以描述对象类型,还可以描述函数类型。使用箭头函数或函数声明来定义函数类型,并指定参数类型和返回类型。
interface Greeter {
(name: string): string; // 函数类型
}
let greet: Greeter = function (name: string) {
return `Hello, ${name}!`;
};
console.log(greet("Alice")); // Hello, Alice!
- 自定义属性:接口可以包含除了已知属性之外的其他属性,这样可以灵活地添加自定义属性。
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相似,同时也增加了一些额外的特性
特点:
- 修饰符(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.
- 抽象类(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...
- 接口约束类(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进阶
高级类型
- 联合类型(Union Types):使用|符号表示,表示一个值可以是多个类型之一。
let age: number | string; // age 可以是 number 类型或 string 类型
age = 25;
age = "25";
- 交叉类型(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"
};
- 类型断言(Type Assertion):用来告诉编译器某个值的具体类型。有两种写法:尖括号写法和as写法。
let value: any = "Hello, TypeScript!";
let length1: number = (<string>value).length; // 使用尖括号写法
let length2: number = (value as string).length; // 使用as写法
- 类型别名(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
};
泛型
基本定义:
-
泛型的语法是在尖括号<>中写入类型参数,通常用T表示。
-
在使用泛型时,可以通过两种方式指定类型:
- 显式指定要使用的类型。
- 通过TS类型推断,让编译器自动推导类型。
-
泛型的作用是临时占位,后续根据传入的类型进行推导。
泛型工具类型: 在TypeScript中,还有一些内置的泛型工具类型,可以帮助我们处理常见的类型操作。
-
Partial:将类型的所有属性变为可选。
-
Required:将类型的所有属性变为必选。
-
Readonly:将类型的所有属性变为只读。
基础操作符: 除了泛型工具类型,TypeScript还提供了一些基础的操作符来处理类型。
-
typeof:用于获取类型的字符串表示。
-
keyof:用于获取类型的所有键(属性名)组成的联合类型。
-
in:用于遍历枚举类型。
-
T[K]:用于索引访问,可以通过类型T和键K获取对应的属性值的类型。
-
extends:用于泛型约束,限制泛型类型必须是某个类型的子类型。
TypeScript实战
声明文件
-
declare:当我们使用第三方库或模块时,如果该库没有提供类型声明文件,我们可以使用declare关键字来创建一个声明文件。声明文件的扩展名通常是.d.ts,并包含对库或模块的类型定义。可以将这些声明文件放在项目中的一个单独目录中,或者使用@types包来获取类型声明。 -
@types:是一种特殊的npm包,用于提供第三方库的类型声明。当我们在使用第三方库时,如果该库本身没有提供类型声明文件,我们可以通过安装对应的@types包来获取类型声明。 -
tsconfig.json文件是TypeScript项目的配置文件,用于指定TypeScript编译器的行为和选项。我们可以在其中定义编译目标、模块解析方式、输出目录等。
后端接口约束
在后端开发中,我们经常需要与数据库、API等交互,并与服务器进行数据传输。使用TypeScript可以对后端接口进行约束,以确保数据的正确性和类型安全性。