目录
1. 函数
函数是一段可以被重复调用的代码块,用于执行特定的任务或计算,并可以返回一个值。
函数具有以下几个重要的特点和作用:
-
代码复用:通过将一段常用的、具有特定功能的代码封装成函数,可以在程序的不同地方多次调用,避免重复编写相同的代码,提高代码的可维护性和可读性。
-
模块化:函数将复杂的程序逻辑分解为较小的、可管理的单元,每个函数专注于完成一个特定的任务,使整个程序结构更清晰、更易于理解和调试。
-
参数传递:函数可以接受输入参数,这些参数为函数提供了执行任务所需的具体数据,使函数具有通用性和灵活性。
-
返回值:函数可以根据其内部的计算和操作返回一个结果,这个结果可以被其他部分的代码使用。
1.1 函数的声明
函数的声明使用 function
关键字。以下是函数声明的基本语法:
function 函数名(参数1, 参数2, 参数3,...) {
// 函数体,包含要执行的语句
return 返回值; // 可选,如果没有返回值可以省略
}
函数声明引入一个函数,包含其名称、参数列表、返回类型和函数体。在函数声明中,必须为每个参数标记类型。如果参数为可选参数,那么允许在调用函数时省略该参数。函数的最后一个参数可以是rest参数。例如
function add(num1:number, num2:number) {
return num1 + num2;
}
1.2 可选参数
1.2.1 参数名?:类型
可选参数的格式可为name?: Type。
function sortNumber(a?: number) {
if (a) {
a++
} else {
console.log('没有值')
}
}
sortNumber() // 没有值
sortNumber(8) // 9
1.2.2 参数名 :类型 = 值
a: number, b: number = 1 b
参数被赋予了默认值 1
,使其成为可选参数。
function sortNumber(b: number,a: number=1 ) {
if (a) {
a+b
}
}
sortNumber(8,9) // 17
sortNumber(8) // 9
这里if(a)利用了隐式转换,感兴趣的可以看看这篇文章
1.3 Rest参数
Rest 参数用于表示不确定数量的参数。它使用 ...
语法来定义。
Rest
参数的语法如下:
function 函数名(...参数名: 参数类型[]): 返回值类型 {
// 函数体
}
例如:
function sum(...numbers: number[]) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4, 5));
1.4 返回类型
返回类型的语法
function 函数名(参数列表): 返回值类型 {
// 函数体
// 返回相应类型的值
}
1.4.1 显示返回
function multiply(a: number, b: number): number {
return a * b;
}
1.4.2 隐示返回
function multiply(a: number, b: number){
return a * b;
}
如果可以从函数体内推断出函数返回类型,则可在函数声明中省略标注返回类型。
1.4.3 无返回类型
function printMessage(message: string): void {
console.log(message);
}
function printMessage(message: string) {
console.log(message);
}
1.5 函数的作用域
函数的作用域决定了变量的可见性和可访问性。
1.5.1全局作用域
在函数外部声明的变量具有全局作用域,可以在整个程序的任何地方访问(除非被同名的局部变量遮蔽)。
let globalVar = 10; // 全局变量
function myFunction() {
console.log(globalVar); // 可以访问全局变量
}
1.5.2局部作用域
在函数内部声明的变量具有局部作用域,只能在该函数内部访问。
function myFunction() {
let localVar = 20; // 局部变量
console.log(localVar);
}
// 这里无法访问 localVar,因为它是函数内部的局部变量
// console.log(localVar); 会报错
1.6函数调用
函数调用是通过使用函数的名称,并传递相应的参数来实现的。
函数名(参数1, 参数2, 参数3,...);
-
函数名
是你要调用的函数的名称。 -
参数
是传递给函数的值或表达式,参数的数量和类型应该与函数定义中指定的相匹配。
function add(a: number, b: number): number {
return a + b;
}
let sum = add(5, 3);
console.log(sum);
1.7函数类型
函数类型用于描述函数的参数类型和返回值类型。
(参数1类型, 参数2类型,...) => 返回值类型
例如
let myFunction: (num1: number, num2: number) => number;
myFunction = (a: number, b: number) => a + b;
通过明确函数类型,可以增强代码的类型安全性和可读性。
1.8 箭头函数/lambda函数
箭头函数(也称为 lambda 函数)是一种简洁的函数表达式形式。
箭头函数的基本语法如下:
(params) => expression // 当函数体只有一个表达式时
(params) => { statements } // 当函数体包含多个语句时
例如:
// 只有一个表达式的箭头函数
let square = (num: number) => num * num;
// 包含多个语句的箭头函数
let multiplyAndLog = (a: number, b: number) => {
let result = a * b;
console.log(result);
return result;
}
箭头函数有以下几个特点:
-
更简洁的语法,减少了传统函数声明的关键字和括号。
-
箭头函数不会创建自己的
this
上下文,它会继承外部作用域的this
值。 -
不能使用
arguments
对象,但可以使用剩余参数(...rest
)来处理不定数量的参数。
箭头函数在处理回调函数、函数式编程等场景中被广泛使用。
1.9 闭包
简单来说,闭包是指一个函数能够记住并访问其外部(包围它的)函数作用域中的变量,即使外部函数已经执行完毕。
具体来说,当一个函数被创建时,它会携带一个环境(包含了其创建时所在作用域中的变量)。如果这个函数在其外部作用域结束后仍然可以被引用和执行,并且能够访问和操作外部作用域中的变量,那么就形成了闭包。
闭包的主要作用包括:
实现私有变量:可以将变量封装在函数内部,外部无法直接访问和修改,只有通过特定的函数接口来操作。
模拟块级作用域:在没有块级作用域的环境中,创建局部作用域。
函数工厂:创建具有特定配置或状态的函数。
实现数据隐藏和封装:保护数据不被随意修改。
function createCounter() {
let count = 0;
return function() {
count++;
console.log(count);
};
}
let counter = createCounter();
counter(); // 输出 1
counter(); // 输出 2
1.10 函数重载
函数重载是指可以定义多个同名但参数类型或参数数量不同的函数。
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
if (typeof a === 'number' && typeof b === 'number') {
return a + b;
} else if (typeof a ==='string' && typeof b ==='string') {
return a.concat(b);
}
}
console.log(add(1, 2));
console.log(add('Hello, ', 'World!'));
2.类
类定义了对象的属性(数据)和方法(行为)。通过类,可以创建多个具有相同结构和行为的对象实例。类具有以下几个主要用途:
-
封装:将数据(属性)和操作这些数据的方法组合在一起,形成一个独立的单元。这有助于隐藏内部实现细节,只暴露必要的接口给外部代码,提高代码的安全性和可维护性。
-
代码复用:可以创建多个类的实例,每个实例都具有类中定义的属性和方法。这样避免了重复编写相同的代码结构和逻辑。
-
组织和结构化代码:将相关的功能和数据集中在一个类中,使代码更具逻辑性和可读性,便于理解和管理大型项目的代码结构。
-
继承:通过继承机制,可以创建子类来扩展和特化父类的功能,实现代码的层次化和模块化。
-
多态:基于继承关系,可以实现多态性,即不同的子类对象可以对相同的方法调用做出不同的响应,增加了代码的灵活性和可扩展性。
-
模拟真实世界的对象:类可以用来对现实世界中的实体进行建模,使得代码更直观地反映问题域的概念和关系。
2.1 字段
字段是类中用于存储数据的成员变量。
2.1.1实例字段
实例字段属于类的实例对象,每个实例都有自己独立的实例字段值。
class Person {
name: string; // 实例字段
}
2.1.2静态字段
静态字段属于类本身,而不是类的实例。通过类名来访问。
class Person {
static count: number = 0; // 静态字段
}
2.1.3字段初始化
可以在字段声明时进行初始化,也可以在构造函数中进行初始化。
2.1.3.1 声明时初始化
class Person {
name: string = "Unknown"; // 初始化实例字段
}
2.1.3.2 构造函数初始化
Alt+inser 快速生成构造函数
Alt+C+G 快速生成构造函数
鼠标右键>生成 快速生成构造函数
class Person {
name: string;
constructor() {
this.name = "John"; // 在构造函数中初始化
}
}
2.1.4getter和setter
getter 和 setter 用于控制对字段的读取和写入操作,提供了一种封装和保护字段的方式。
class Person {
private _age: number;
get age(): number {
return this._age;
}
set age(value: number) {
if (value >= 0) {
this._age = value;
}
}
}
2.2 方法
方法是类中定义的用于执行特定操作的函数。
2.2.1实例方法
实例方法属于类的实例对象,可以访问实例字段和其他实例方法。
class Person {
name: string;
introduce() { // 实例方法
console.log(`我叫 ${this.name}`);
}
}
2.2.2静态方法
静态方法属于类本身,通过类名调用,不能访问实例字段和实例方法。
class Person {
static createPerson(name: string) { // 静态方法
return new Person(name);
}
}
2.2.3继承
通过继承,可以创建子类来复用和扩展父类的属性和方法。
2.2.3.1 extends
当一个类(子类)使用 extends
关键字继承另一个类(父类)时,子类会继承父类的属性和方法。
以下是一个示例:
class Parent {
public parentProperty: string = "这是父类的属性";
public parentMethod() {
console.log("这是父类的方法");
}
}
class Child extends Parent {
// 子类可以添加自己的属性和方法
public childProperty: number = 42;
public childMethod() {
console.log("这是子类的方法");
}
}
let childInstance = new Child();
// 可以访问父类的属性和方法
console.log(childInstance.parentProperty);
childInstance.parentMethod();
// 也可以访问子类自己的属性和方法
console.log(childInstance.childProperty);
childInstance.childMethod();
2.2.3.2 implements
implements
关键字用于类实现接口。
接口定义了一组方法和属性的签名,但没有具体的实现。当一个类使用 implements
关键字时,它必须提供接口中定义的所有方法和属性的实现。
以下是一个示例
interface IPrintable {
print(): void;
}
class Document implements IPrintable {
print(): void {
console.log("打印文档");
}
}
2.2.4父类访问
在子类中,可以通过 super
关键字来访问父类的方法和属性。
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move() {
console.log(`${this.name} 在移动`);
}
}
class Dog extends Animal {
constructor(name: string) {
super(name); // 调用父类的构造函数
}
bark() {
console.log("汪汪汪");
super.move(); // 调用父类的方法
}
}
2.2.5方法重写
子类可以重写父类的方法,以实现不同的行为。
class Animal {
move() {
console.log("动物在移动");
}
}
class Dog extends Animal {
move() { // 重写父类的 move 方法
console.log("狗在奔跑");
}
}
2.2.6方法重载签名
方法重载是指定义多个同名但参数类型或参数数量不同的方法。
class Calculator {
add(a: number, b: number): number;
add(a: string, b: string): string;
add(a: any, b: any): any {
if (typeof a === 'number' && typeof b === 'number') {
return a + b;
} else if (typeof a ==='string' && typeof b ==='string') {
return a + b;
}
}
}
2.3构造函数
构造函数是在创建类的新实例时自动调用的特殊方法,用于初始化对象的属性。
2.3.1派生类的构造函数
当存在类的继承关系时,派生类(子类)需要在其构造函数中调用父类的构造函数来正确初始化从父类继承的属性。可以使用 super
关键字来实现。
示例:
class Parent {
constructor(public name: string) {}
}
class Child extends Parent {
constructor(name: string, public age: number) {
super(name); // 调用父类的构造函数
}
}
2.3.2构造函数重载签名
类似于普通方法的重载,构造函数也可以有多个不同参数的版本
示例:
class Person {
constructor();
constructor(name: string);
constructor(name?: string) {
if (name) {
this.name = name;
}
}
}
2.4可见性修饰符
类的方法和属性都可以使用可见性修饰符。可见性修饰符用于控制类成员的访问权限。
可见性修饰符包括:private、protected和public。默认可见性为public。
2.4.1 public(公用)
public
修饰的成员可以在类的内部、子类以及类的实例外部被访问。这是默认的可见性,如果没有显式指定修饰符,成员默认为 public
。
2.4.2 private(私有)
private
修饰的成员只能在其所在的类内部被访问,子类和类的实例外部都无法访问。
2.4.3protected(受保护)
protected
修饰的成员可以在其所在的类内部以及子类中被访问,但在类的实例外部无法访问。
class Animal {
// 公共属性,可以在任何地方访问
public name: string;
// 受保护属性,可以在当前类和子类中访问
protected age: number;
// 私有属性,只能在当前类中访问
private weight: number;
constructor(name: string, age: number, weight: number) {
this.name = name;
this.age = age;
this.weight = weight;
}
public move() {
console.log(`${this.name} is moving.`);
}
protected grow() {
this.age++;
console.log(`The age of ${this.name} is increased to ${this.age}.`);
}
private eat() {
console.log(`${this.name} is eating.`);
}
}
class Dog extends Animal {
constructor(name: string, age: number, weight: number) {
super(name, age, weight);
}
public bark() {
console.log(`${this.name} is barking.`);
// 可以访问父类的公共属性
console.log(`The name of the dog is ${this.name}.`);
// 可以访问父类的受保护属性
console.log(`The age of the dog is ${this.age}.`);
// 无法访问父类的私有属性,以下代码会报错
// console.log(`The weight of the dog is ${this.weight}.`);
// 可以调用父类的公共方法
this.move();
// 可以调用父类的受保护方法
this.grow();
// 无法调用父类的私有方法,以下代码会报错
// this.eat();
}
}
let myDog = new Dog('Buddy', 3, 15);
myDog.bark();
在上述示例中:
name
是公共属性,可以在类的外部和子类中直接访问和修改。
age
是受保护属性,可以在父类和子类内部访问和修改,但在类的外部无法直接访问。
weight
是私有属性,只能在父类内部访问和修改,子类和类的外部都无法直接访问。同样,对于方法,
move
是公共方法,grow
是受保护方法,eat
是私有方法,其访问规则与属性类似。
2.5对象字面量
对象字面量是一种创建对象的简洁方式,通过使用花括号 {}
并指定属性和值来创建对象。
let person = {
name: "Alice",
age: 25
};
2.5.1Record类型的对象字面量
泛型Record<K, V>用于将类型(键类型)的属性映射到另一个类型(值类型)。常用对象字面量来初始化该类型的值:
type PersonRecord = Record<string, string>;
const person: PersonRecord = {
"name": "Alice",
"city": "New York"
};