TS泛型,原来就这?

news2025/1/8 19:34:18

一、泛型是什么?有什么作用?

当我们定义一个变量不确定类型的时候有两种解决方式:

  • 使用any
    使用any定义时存在的问题:虽然知道传入值的类型但是无法获取函数返回值的类型;另外也失去了ts类型保护的优势

  • 使用泛型
    泛型指的是在定义函数/接口/类型时,不预先指定具体的类型,而是在使用的时候在指定类型限制的一种特性。

  • 设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是:类的实例成员、类的方法、函数参数和函数返回值。

为了便于大家更好地理解上述的内容,我们来举个例子,在这个例子中,我们将一步步揭示泛型的作用。首先我们来定义一个通用的 identity 函数,该函数接收一个参数并直接返回它:

function identity (value) {
	return value;
}

console.log(identity(1)) // 1

现在,我们将 identity 函数做适当的调整,以支持 TypeScript 的 Number 类型的参数:

function identity (value: Number) : Number {
	return value;
}

console.log(identity(1)) // 1

这里 identity 的问题是我们将 Number 类型分配给参数和返回类型,使该函数仅可用于该原始类型。但该函数并不是可扩展或通用的,很明显这并不是我们所希望的。

我们确实可以把 Number 换成 any,我们失去了定义应该返回哪种类型的能力,并且在这个过程中使编译器失去了类型保护的作用。我们的目标是让 identity 函数可以适用于任何特定的类型,为了实现这个目标,我们可以使用泛型来解决这个问题,具体实现方式如下:

function identity <T>(value: T) : T {
	return value;
}

console.log(identity<Number>(1)) // 1

对于刚接触 TypeScript 泛型的读者来说,首次看到 语法会感到陌生。但这没什么可担心的,就像传递参数一样,我们传递了我们想要用于特定函数调用的类型。

在这里插入图片描述
参考上面的图片,当我们调用 identity(1) ,Number 类型就像参数 1 一样,它将在出现 T 的任何位置填充该类型。图中< T >内部的 T 被称为类型变量,它是我们希望传递给 identity 函数的类型占位符,同时它被分配给 value 参数用来代替它的类型:此时 T 充当的是类型,而不是特定的 Number 类型。

其中 T 代表 Type,在定义泛型时通常用作第一个类型变量名称。但实际上 T 可以用任何有效名称代替。除了 T 之外,以下是常见泛型变量代表的意思:

  • K(Key):表示对象中的键类型;
  • V(Value):表示对象中的值类型;
  • E(Element):表示元素类型;

其实并不是只能定义一个类型变量,我们可以引入希望定义的任何数量的类型变量。比如我们引入一个新的类型变量 U,用于扩展我们定义的 identity 函数:

function identity <T, U>(value: T, message: U) : T {
  	console.log(message);
 	return value;
}

console.log(identity<Number, string>(68, "Semlinker"));

在这里插入图片描述
除了为类型变量显式设定值之外,一种更常见的做法是使编译器自动选择这些类型,从而使代码更简洁。我们可以完全省略尖括号,比如:

function identity <T, U>(value: T, message: U) : T {
	console.log(message);
	return value;
}

console.log(identity(68, "Semlinker"));

如你所见,该函数接收你传递给它的任何类型,使得我们可以为不同类型创建可重用的组件。现在我们再来看一下 identity 函数:

function identity <T, U>(value: T, message: U) : T {
  	console.log(message);
  	return value;
}

相比之前定义的 identity 函数,新的 identity 函数增加了一个类型变量 U,但该函数的返回类型我们仍然使用 T。如果我们想要返回两种类型的对象该怎么办呢?针对这个问题,我们有多种方案,其中一种就是使用元组,即为元组设置通用的类型:

function identity <T, U>(value: T, message: U) : [T, U] {
	return [value, message];
}

虽然使用元组解决了上述的问题,但有没有其它更好的方案呢?答案是有的,你可以使用泛型接口

二、泛型用法

1、在函数中使用泛型

function test <T> (arg:T):T{
  console.log(arg);
  return arg;
}
test<number>(111);// 返回值是number类型的 111
test<string | boolean>('hahaha')//返回值是string类型的 hahaha
test<string | boolean>(true);//返回值是布尔类型的 true

// 使用方式类似于函数传参,传什么数据类型,T就表示什么数据类型, 使用表示,T也可以换成任意字符串。

2、在接口中使用泛型

// 注意,这里写法是定义的方法!!!
interface Search {
  <T,Y>(name:T,age:Y):T
}

let fn:Search = function <T, Y>(name: T, id:Y):T {
  console.log(name, id)
  return name;
}
fn('li',11);//编译器会自动识别传入的参数,将传入的参数的类型认为是泛型指定的类型

为了解决刚刚上面提到的问题,首先让我们创建一个用于的 identity 函数通用 Identities 接口:

interface Identities<V, M> {
  value: V,
  message: M
}

在上述的 Identities 接口中,我们引入了类型变量 V 和 M,来进一步说明有效的字母都可以用于表示类型变量,之后我们就可以将 Identities 接口作为 identity 函数的返回类型:

function identity<T, U> (value: T, message: U): Identities<T, U> {
  console.log(value + ": " + typeof (value));
  console.log(message + ": " + typeof (message));
  let identities: Identities<T, U> = {
    value,
    message
  };
  return identities;
}

console.log(identity(68, "Semlinker"));

以上代码成功运行后,在控制台会输出以下结果:

68: number
Semlinker: string
{value: 68, message: "Semlinker"}

泛型除了可以应用在函数和接口之外,它也可以应用在类中,下面我们就来看一下在类中如何使用泛型。

3、在类中使用泛型

class Animal<T> {
 name:T;
 constructor(name: T){
   this.name = name;
 }
 action<T>(say:T) {
   console.log(say)
 }
}
let cat = new Animal('cat');
cat.action('mimi')

在类中使用泛型也很简单,我们只需要在类名后面,使用 <T, …> 的语法定义任意多个类型变量,具体示例如下:

interface GenericInterface<U> {
  value: U
  getIdentity: () => U
}

class IdentityClass<T> implements GenericInterface<T> {
  value: T

  constructor(value: T) {
    this.value = value
  }

  getIdentity(): T {
    return this.value
  }

}

const myNumberClass = new IdentityClass<Number>(68);
console.log(myNumberClass.getIdentity()); // 68

const myStringClass = new IdentityClass<string>("Semlinker!");
console.log(myStringClass.getIdentity()); // Semlinker!

接下来我们以实例化 myNumberClass 为例,来分析一下其调用过程:

  • 在实例化 IdentityClass 对象时,我们传入 Number 类型和构造函数参数值 68;
  • 之后在 IdentityClass 类中,类型变量 T 的值变成 Number 类型;
  • IdentityClass 类实现了 GenericInterface< T >,而此时 T 表示 Number 类型,因此等价于该类实现了 GenericInterface< Number > 接口;
  • 而对于 GenericInterface< T >接口来说,类型变量 U 也变成了 Number。这里我有意使用不同的变量名,以表明类型值沿链向上传播,且与变量名无关。
  • 泛型类可确保在整个类中一致地使用指定的数据类型。比如,你可能已经注意到在使用 Typescript 的 React 项目中使用了以下约定:
type Props = {
  className?: string
   ...
};

type State = {
  submitted?: bool
   ...
};

class MyComponent extends React.Component<Props, State> {
   ...
}

在以上代码中,我们将泛型与 React 组件一起使用,以确保组件的 props 和 state 是类型安全的。

相信看到这里一些读者会有疑问,我们在什么时候需要使用泛型呢?通常在决定是否使用泛型时,我们有以下两个参考标准:

  • 当你的函数、接口或类将处理多种数据类型时;
  • 当函数、接口或类在多个地方使用该数据类型时。

很有可能你没有办法保证在项目早期就使用泛型的组件,但是随着项目的发展,组件的功能通常会被扩展。这种增加的可扩展性最终很可能会满足上述两个条件,在这种情况下,引入泛型将比复制组件来满足一系列数据类型更干净。

我们将在本文的后面探讨更多满足这两个条件的用例。不过在这样做之前,让我们先介绍一下 Typescript 泛型提供的其他功能。

三、泛型约束

interface Person {
  name:string;
  age:number;
}
function student<T extends Person>(arg:T):T {
  return arg;
}

student({name:'lili'});//类型 "{ name: string; }" 中缺少属性 "age",但类型 "Person" 中需要该属性
student({ name: "lili" , age:'11'});//不能将类型“string”分配给类型“number”
student({ name: "lili" , age:11});

有时我们可能希望限制每个类型变量接受的类型数量,这就是泛型约束的作用。下面我们来举几个例子,介绍一下如何使用泛型约束。

1、确保属性存在

一个很好的例子是在处理字符串或数组时,我们会假设 length 属性是可用的。让我们再次使用 identity 函数并尝试输出参数的长度:

function identity<T>(arg: T): T {
  console.log(arg.length); // Error
  return arg;
}

在这种情况下,编译器将不会知道 T 确实含有 length 属性,尤其是在可以将任何类型赋给类型变量 T 的情况下。我们需要做的就是让类型变量 extends 一个含有我们所需属性的接口,比如这样:

interface Length {
  length: number;
}

function identity<T extends Length>(arg: T): T {
  console.log(arg.length); // 可以获取length属性
  return arg;
}

在这里插入图片描述
T extends Length 用于告诉编译器,我们支持已经实现 Length 接口的任何类型。之后,当我们使用不含有 length 属性的对象作为参数调用 identity 函数时,TypeScript 会提示相关的错误信息:

identity(68); // Error
// Argument of type '68' is not assignable to parameter of type 'Length'.(2345)

此外,我们还可以使用 , 号来分隔多种约束类型,比如:<T extends Length, Type2, Type3>。而对于上述的 length 属性问题来说,如果我们显式地将变量设置为数组类型,也可以解决该问题,具体方式如下:

function identity<T>(arg: T[]): T[] {
   console.log(arg.length);  
   return arg; 
}

// or
function identity<T>(arg: Array<T>): Array<T> {      
  console.log(arg.length);
  return arg; 
}

2、检查对象上的键是否存在

泛型约束的另一个常见的使用场景就是检查对象上的键是否存在。不过在看具体示例之前,我们得来了解一下 keyof 操作符,keyof 操作符是在 TypeScript 2.1 版本引入的,该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。 “耳听为虚,眼见为实”,我们来举个 keyof 的使用示例:

interface Person {
  name: string;
  age: number;
  location: string;
}

type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[];  // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string | number

通过 keyof 操作符,我们就可以获取指定类型的所有键,之后我们就可以结合前面介绍的 extends 约束,即限制输入的属性名包含在 keyof 返回的联合类型中。具体的使用方式如下:

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

在以上的 getProperty 函数中,我们通过 K extends keyof T 确保参数 key 一定是对象中含有的键,这样就不会发生运行时错误。这是一个类型安全的解决方案,与简单调用 let value = obj[key]; 不同。

下面我们来看一下如何使用 getProperty 函数:

enum Difficulty {
  Easy,
  Intermediate,
  Hard
}

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

let tsInfo = {
   name: "Typescript",
   supersetOf: "Javascript",
   difficulty: Difficulty.Intermediate
}
 
let difficulty: Difficulty = 
  getProperty(tsInfo, 'difficulty'); // OK

let supersetOf: string = 
  getProperty(tsInfo, 'superset_of'); // Error

在以上示例中,对于 getProperty(tsInfo, ‘superset_of’) 这个表达式,TypeScript 编译器会提示以下错误信息:

Argument of type '"superset_of"' is not assignable to parameter of type 
'"difficulty" | "name" | "supersetOf"'.(2345)

很明显通过使用泛型约束,在编译阶段我们就可以提前发现错误,大大提高了程序的健壮性和稳定性。接下来,我们来介绍一下泛型参数默认类型。

3、数组泛型

let arr:Array<number> =[1,2,3] === let arr:number[]=[1,2,3]

四、泛型参数默认类型

在 TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推断出类型时,这个默认类型就会起作用。

泛型参数默认类型与普通函数默认值类似,对应的语法很简单,即 <T=Default Type>,对应的使用示例如下:

interface A<T=string> {
  name: T;
}

const strA: A = { name: "Semlinker" };
const numB: A<number> = { name: 101 };

泛型参数的默认类型遵循以下规则:

  • 有默认类型的类型参数被认为是可选的。
  • 必选的类型参数不能在可选的类型参数后。
  • 如果类型参数有约束,类型参数的默认类型必须满足这个约束。
  • 当指定类型实参时,你只需要指定必选类型参数的类型实参。 未指定的类型参数会被解析为它们的默认类型。
  • 如果指定了默认类型,且类型推断无法选择一个候选类型,那么将使用默认类型作为推断结果。
  • 一个被现有类或接口合并的类或者接口的声明可以为现有类型参数引入默认类型。
  • 一个被现有类或接口合并的类或者接口的声明可以引入新的类型参数,只要它指定了默认类型。

五、泛型条件类型

在 TypeScript 2.8 中引入了条件类型,使得我们可以根据某些条件得到不同的类型,这里所说的条件是类型兼容性约束。尽管以上代码中使用了 extends 关键字,也不一定要强制满足继承关系,而是检查是否满足结构兼容性。

条件类型会以一个条件表达式进行类型关系检测,从而在两种类型中选择其一:

T extends U ? X : Y

以上表达式的意思是:若 T 能够赋值给 U,那么类型是 X,否则为 Y。在条件类型表达式中,我们通常还会结合 infer 关键字,实现类型抽取:

interface Dictionary<T = any> {
  [key: string]: T;
}
 
type StrDict = Dictionary<string>

type DictMember<T> = T extends Dictionary<infer V> ? V : never
type StrDictMember = DictMember<StrDict> // string

在上面示例中,当类型 T 满足 T extends Dictionary 约束时,我们会使用 infer 关键字声明了一个类型变量 V,并返回该类型,否则返回 never 类型。

  • 在 TypeScript 中,never 类型表示的是那些永不存在的值的类型。 例如, never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。
  • 另外,需要注意的是,没有类型是 never 的子类型或可以赋值给 never 类型(除了 never 本身之外)。 即使 any 也不可以赋值给 never。

除了上述的应用外,利用条件类型和 infer 关键字,我们还可以方便地实现获取 Promise 对象的返回值类型,比如:

async function stringPromise() {
  return "Hello, Semlinker!";
}

interface Person {
  name: string;
  age: number;
}

async function personPromise() {
  return { name: "Semlinker", age: 30 } as Person;
}

type PromiseType<T> = (args: any[]) => Promise<T>;
type UnPromisify<T> = T extends PromiseType<infer U> ? U : never;

type extractStringPromise = UnPromisify<typeof stringPromise>; // string
type extractPersonPromise = UnPromisify<typeof personPromise>; // Person

六、泛型工具类型

1、Partial

partial< T >的作用就是将某个类型中的属性全部变为可选项?

interface Person {
  name:string;
  age:number;
}
function student<T extends Person>(arg: Partial<T>):Partial<T> {
  return arg;
}
interface Todo {
  title: string;
  description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  return { ...todo, ...fieldsToUpdate };
}

const todo1 = {
  title: "organize desk",
  description: "clear clutter"
};

const todo2 = updateTodo(todo1, {
  description: "throw out trash"
});

在上面的 updateTodo 方法中,我们利用 Partial< T > 工具类型,定义 fieldsToUpdate 的类型为 Partial< Todo >,即:

{
   title?: string | undefined;
   description?: string | undefined;
}

2、Record

Record<K extends keyof any, T> 的作用是将 K 中所有的属性的值转化为 T 类型。

interface PageInfo {
  title: string
}
type Page = 'home'|'about'|'other';
const x: Record<Page, PageInfo> = {
  home: { title: "xxx" },
  about: { title: "aaa" },
  other: { title: "ccc" },
};

3、Pick

Pick<T, K extends keyof T>的作用是将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型,示例:

interface Todo {
  title:string,
  desc:string,
  time:string
}
type TodoPreview = Pick<Todo, 'title'|'time'>;
const todo: TodoPreview ={
  title:'吃饭',
  time:'明天'
}

4、Exclude

Exclude<T,U>的作用是将某个类型中属于另一个类型的属性移除掉,示例:

type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
const t:T0 ='b';
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number

5、ReturnType

returnType< T >的作用是用于获取函数T的返回类型,示例:

type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
type T2 = ReturnType<<T>() => T>; // {}
type T3 = ReturnType<<T extends U, U extends number[]>() => T>; // number[]
type T4 = ReturnType<any>; // any
type T5 = ReturnType<never>; // any
type T6 = ReturnType<string>; // Error
type T7 = ReturnType<Function>; // Error

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

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

相关文章

记一次线上es慢查询导致的服务不可用

现象 某日线上业务同学反馈订单列表查询页面一直loding&#xff0c;然后提示请求超时&#xff0c;几分钟之后恢复正常 接到报障之后&#xff0c;马上根据接口URL&#xff0c;定位到了请求链路&#xff0c;发现是es查询超时&#xff0c;这里我们的业务订单表数据是由几百万的&a…

【数据结构】时间复杂度和空间复杂度以及相关OJ题的详解分析

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;数据结构 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录1.算法效率1.1 如何衡…

独家 | Gen-1——可以改变视频风格的AI模型

翻译&#xff1a;吴振东校对&#xff1a;张睿毅本文约1000字&#xff0c;建议阅读3分钟 本文简单介绍了Runway公司的发展史&#xff0c;以及他们新推出的生成式AI模型Gen-1&#xff0c;可用于通过应用文本提示或者参考图像所指定的任意风格&#xff0c;将现有视频转换为新视频。…

php mysql高校教材管理系统

我的目标就是在于开发一个功能实用、操作方便&#xff0c;简单明了的管理系统&#xff1b;其能够录入教师个人的信息&#xff0c;教导主任信息&#xff0c;在操作上能够完成诸如添加、修改、删除、按各种条件进行查询、等方面的工作&#xff0c;基本满足学校的日常业务的需求. …

System V|共享内存基本通信框架搭建|【超详细的代码解释和注释】

前言 那么这里博主先安利一下一些干货满满的专栏啦&#xff01; 手撕数据结构https://blog.csdn.net/yu_cblog/category_11490888.html?spm1001.2014.3001.5482这里包含了博主很多的数据结构学习上的总结&#xff0c;每一篇都是超级用心编写的&#xff0c;有兴趣的伙伴们都支…

string和自动推断类型

欢迎来观看温柔了岁月.c的博客目前设有C学习专栏C语言项目专栏数据结构与算法专栏目前主要更新C学习专栏&#xff0c;C语言项目专栏不定时更新待C专栏完毕&#xff0c;会陆续更新C项目专栏和数据结构与算法专栏一周主要三更&#xff0c;星期三&#xff0c;星期五&#xff0c;星…

【项目管理】项目进度管理中的逻辑关系

项目的进度管理是项目核心管理之一&#xff0c;通过合理的进度安排&#xff0c;制定出科学可行的分项工期表&#xff0c;并条理清晰的显示出项目进度之间的逻辑关系。 1、目标是计划的灵魂 进度计划必须按照确定的项目总进度要求进行编制&#xff0c;了解项目总目标和整体安…

网络安全——数据链路层安全协议(2)

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.局域网数据链路层安全协议 1.IEEE 802.10 &#xff08;1&#xff09;IEE…

JavaWeb HTTP和Maven

4、Http 4.1、什么是HTTP HTTP&#xff08;超文本传输协议&#xff09;是一个简单的请求-响应协议&#xff0c;它通常运行在TCP之上。 文本&#xff1a;html&#xff0c;字符串&#xff0c;~ ….超文本&#xff1a;图片&#xff0c;音乐&#xff0c;视频&#xff0c;定位&am…

登峰造极,师出造化,Pytorch人工智能AI图像增强框架ControlNet绘画实践,基于Python3.10

人工智能太疯狂&#xff0c;传统劳动力和内容创作平台被AI枪毙&#xff0c;弃尸尘埃。并非空穴来风&#xff0c;也不是危言耸听&#xff0c;人工智能AI图像增强框架ControlNet正在疯狂地改写绘画艺术的发展进程&#xff0c;你问我绘画行业未来的样子&#xff1f;我只好指着Cont…

jdbc模板的基本使用

1.JdbcTemplate的开发步骤 <1>导入spring-jdbc和spring-tx坐标 <2>创建数据库表和实体 <3>创建JdbcTemplate对象 <4>执行数据库 2.JdbcTemplate快速入门 <1>导入坐标 <dependency><groupId>org.springframework</groupId><…

【Python学习笔记】第十七节 Python 异常处理

Python 异常在任何一种编程语言中&#xff0c;都会有异常处理机制&#xff0c;python也不例外&#xff0c;它提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误。Python 异常处理异常的概念&#xff1a;在程序运行过程中&#xff0c;由于代码错误或者运行环境…

Java数据结构与算法——手撕LRULFU算法

LRU算法 力扣146&#xff1a;https://leetcode-cn.com/problems/lru-cache/ 讲解视频&#xff1a;https://www.bilibili.com/video/BV1Hy4y1B78T?p65&vd_source6f347f8ae76e7f507cf6d661537966e8 LRU是Least Recently Used的缩写&#xff0c;是一种常用的页面置换算法&…

Typora图床配置:Typora + PicGo + 阿里云OSS

文章目录一、前景提要二、相关链接三、搭建步骤1. 购买阿里云对象存储OSS2. 对象存储OSS&#xff1a;创建Bucket3. 阿里云&#xff1a;添加OSS访问用户及权限4. 安装Typora5. 配置PicGo方法一&#xff1a;使用PicGo-Core (Command line)方法二&#xff1a;使用PicGo(app)6. 最后…

C语言深度剖析:关键字

C语言深度剖析:关键字C语言深度剖析:关键字前言定义与声明&#xff08;补充内容&#xff09;最宏大的关键字-auto最快的关键字-register关键字static被冤枉的关键字-sizeof整型在内存中的存储原码、反码、补码大小端补充理解变量内容的存储和取出为什么都是补码整型取值范围关于…

多线程的初识和创建

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; ✨每日一语&#xff1a;知不足而奋进&#xff0c;望远山而前行。 目 录&#x1f4a4;一. 认识线程&#xff08;Thread&#xff09;&#x1f34e;1. 线程的引入&#x1f34f;2. 线程…

【计算机网络:自顶向下方法】Chapter4 网络层:数据平面

Chapter44.1 网络层概述4.1.1 网络层服务4.1.2 网络层的主要功能转发&#xff08;局部&#xff09;路由选择&#xff08;全局&#xff09;4.1.3 控制平面和数据平面传统方式SDN方式4.1.4 网络服务模型4.2 路由器组成4.2.1 路由器结构概况4.2.2 转发方式4.2.3 输入端口处理与基于…

什么是api应用程序接口?

API:应用程序接口(API:Application Program Interface) 应用程序接口是一组定义、程序及协议的集合&#xff0c;通过 API 接口实现计算机软件之间的相互通信。API 的一个主要功能是提供通用功能集。程序员通过调用 API 函数对应用程序进行开发&#xff0c;可以减轻编程任务。…

【华为OD机试模拟题】用 C++ 实现 - TLV 编码(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明TLV 编码题目输入输出描述示例一输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看…

大数据技术之Hadoop

第1章 Hadoop概述1.1 Hadoop是什么1.2 Hadoop发展历史&#xff08;了解&#xff09;1.3 Hadoop三大发行版本&#xff08;了解&#xff09;Hadoop三大发行版本&#xff1a;Apache、Cloudera、Hortonworks。Apache版本最原始&#xff08;最基础&#xff09;的版本&#xff0c;对于…