详解 TS 中的子类型兼容性

news2024/11/18 15:32:35

简介

在写 TypeScript 代码时经常遇到类型检查不通过的问题,这些问题根据编译器给出的错误提示以及修改建议多数可以快速修复。本文讲解的内容是编译器进行类型检查时的兼容性相关检查规则,这些规则在 TypeScript 语言背后默默发挥作用。不了解这些规则的话,在遇到兼容性报错只会根据错误提示来修复,若清楚了编译器的子类型赋值兼容性、赋值兼容性等规则,则可以及早规避相关代码错误。

就是说,本文讲的内容是 TypeScript 类型系统的内部工作方式与原理,掌握之后可以做到 “在编码时将代码在你的大脑里面运行一遍,不用依赖于编译器的类型检查”

赋值兼容性与子类型兼容性

在进行赋值、函数调用时传递参数,TypeScript 会对变量进行类型兼容性检查,若类型兼容性检查通过,则说明满足赋值兼容性要求。

一般情况下,若满足子类型兼容性,则一定满足赋值兼容性;然而,满足赋值兼容性,并不一定满足子类型兼容性。

赋值兼容性特例

下面三种情况下,满足赋值兼容性,但是不满足子类型兼容性。

  • any 类型 下例中变量 tT 类型,any 类型的变量 a 赋值给变量 t 不会导致类型错误,满足赋值兼容性。any 类型为[[顶端类型]],顶端类型是所有类型的超类型,不是 T 的子类型。
interface T {
 name: string;
}
declare let t: T
declare const a: any
t = a 
  • 数字枚举类型 下例中变量 anumber 类型,变量 e 为枚举类型 E 赋值给 a 不会导致类型错误,满足赋值兼容性。这里 number 类型能够赋值给数字枚举类型,但是 number 类型却不是数字枚举类型的子类型,不满足子类型兼容性。
enum E {
 A,
 B,
 C
}
let a = 5
declare let e: E
e = a 
  • 带有可选属性的对象类型 下例中 S 类型能够赋值给 T 类型,满足赋值兼容性,但是 S 类型并不是 T 类型的子类型。因为按照子类型兼容性的要求,若 T 类型上所有的属性(包括可选属性)在 S 类型上都能找到,才算 ST 的子类型,这里 S 上没有 T 上存在的可选属性 b,所以不满足子类型兼容性。
interface T {
 a: string;
 b?: string;
}

interface S {
    a: string;
}
declare let t: T
declare let s: S
t = s 

子类型与赋值类型差异的原因

既然上面三种情况不满足子类型兼容性,那为什么 TypeScript 又允许其满足赋值兼容性呢?

情况一:any 类型

any 类型是[[顶端类型]],根据类型论的解释:顶端类型是所有类型的超类型,确实不会是任何类型的子类型。这属于 TypeScript 类型系统的特例,目的应该是确保现有的 js 代码在向 ts 代码转移时更容易,可以非常快速的通过 any 类型来绕过 TypeScript 中子类型兼容性限制

比如:现有一份功能都已实现的 js 代码,给所有变量 any 类型,基本就可以直接变为 ts 代码,同时类型兼容性还不报错。不过这样一来,TypeScript 也就变成了 AnyScript 了: )。

情况二:数字枚举类型

其实,数字枚举类型被编译为 js 后,在运行时的值就是 number 类型,见下图。之所以,允许 number 类型被赋值给数字枚举类型,也是基于向现有代码兼容的考虑。

情况三:带有可选属性的对象类型

TypeScript 中的可选属性修饰符从类型的角度来看其实是给该属性值类型联合了一个 undefined 类型,而 js 中访问对象上不存在的属性和属性值为 undefined 都会得到 undefined 值。在 js 中多数情况下不使用 hasOwnProperty 方法来判断对象的属性是否存在,是没法区分属性不存在还是属性值为 undefined

TypeScript 的类型系统是在编译时检查的,编译后的代码不存在类型相关代码,所以为了与现有 js 代码兼容保留了带有可选属性的对象类型赋值兼容性。就是编译后代码赋值不影响代码执行,但是带有可选属性的对象类型在进行子类型关系检查时是不通过的。

子类型兼容性

据面向对象程序设计中[[里氏替换原则]]描述,程序中任何使用了超(父)类型的地方都可以使用其子类型进行替换。TypeScript 中的子类型兼容性体现的就是这一原则,而这也正是多态。

多态

多态在类型论中指的是:相同的消息在发送给不同对象时,系统可以根据对象类型不同,分别引发对应类型的方法。

例如,电脑给打印机发出打印消息,彩色打印机和黑白打印机分别执行打印方法,各自打印出彩色照片和黑白照片。

那么,如果把“打印”这个消息发送给到非打印机设备,比如键盘,那么会怎样呢?

当然是不能执行打印方法,不能正常打印出照片的。那系统是怎么知道键盘不能接受打印消息呢?这就涉及到子类型兼容性了,不能执行打印方法的键盘相当于与打印机不兼容。

在 java 语言中打印机相当于是一个接口,描述了打印机所具备的基本功能(方法),不同类型打印机必须实现了打印机这个接口才是与打印机兼容的。

在 TypeScript 中,也存在基于接口实现的子类型兼容性判断,由于要与 js 语言兼容以及 js 语法特性的历史原因,TypeScript 又对子类型兼容检查制定了更详细的规则。

符号约定

子类型兼容性形容的是子类型与超类型的关系(为了便于描述,后文也称作子类型关系),这里把这个关系用符号进行一下约定,便于描述。

若类型 S 是类型 T 的子类型,则用符号表示为:

S <: T 

任意类型都是自身的子类型和超类型,称为自反性,用符号表示为:

S <: S 且 S :> S 

像类的继承一样,子类型与超类型关系具有传递性,用符号表示为:

若
R <: S <: T
则
R <: T 

基本类型

基本类型主要包括原始类型、顶端类型、尾端类型,其子类型关系总结如下:

1.never <: undefined <: null <: [原始类型] number bigint boolean string symbol <: [顶端类型] any unknown2.字面量类型 <: 相应的原始类型1.true <: boolean2.‘a’ <: string3.0 <: number4.0n <: bigint5.Symbol() <: symbol
3.枚举类型 <: number用图表示如下:

函数类型

函数类型子类型关系的比较不同于上面简单类型的子类型关系比较,因为函数类型由参数类型、返回值类型构成。

函数参数数量

TypeScript 在检查函数子类型关系时,编译器将先检查函数参数数量,具体检查规则如下。

  • 规则一:如果 ST 的子类型,即:S <: T,则 S 中所有必选参数必须能够在 T 中找到对应的参数,即 S 中必选参数的个数不能多于 T 中的参数个数。

注意:这一点和对象类型的子类型关系刚好相反,后文会讲到。

type S = (a: number) => void;
type T = (x: number, y: number) => void; 
  • 规则二:如果 ST 的子类型,即:S <: T,则 T 中的可选参数会计入参数总数,不区分可选参数还是必选参数。
type S = (a: number) => void;
type T = (x?: number, y?: number) => void; 
  • 规则三:T 中的剩余参数会被当做无穷多个可选参数并计入参数总数,这相当于不进行参数个数检查,因为 S 的参数个数不可能比无穷多还多。
type S = (a: number) => void;
type T = (...x: number[]) => void; 
  • 规则四:子类型 S 中的可选参数不计入参数总数,即 S 中可以存在多余的可选参数。
type S = (a: number, b?: number) => void;
type T = (x: number) => void; 
  • 规则五:子类型 S 中的剩余参数不计入参数总数。
type S = (a: number, ...b: number[]) => void;
type T = (x: number) => void; 

函数参数类型

TypeScript 检查完函数数量后继续检查函数参数类型,分为两种检查模式:非严格函数类型检查模式(默认模式)严格函数类型检查模式,可以通过 --strictFunctionTypes 编译选项来配置。

非严格函数类型检查模式 该模式下函数参数类型与函数类型是双变关系(双变关系是[[变型]]关系之一)。若函数类型 S 是函数类型 T 的子类型,那么 S 的参数类型必须是 T 中对应参数类型的子类型或者超类型,即只要函数对应位置参数满足子类型关系即可,与子类型关系的方向无关。

// 非严格函数类检查
type S = (a: 0 | 1) => number;
type T = (b: number) => number; 

这里参数 ab 的子类型,参数 ba 的超类型,因此 S 是 T 的子类型。

严格函数类型检查模式 该模式下函数参数类型与函数类型是逆变关系。若函数类型 S 是函数类型 T 的子类型,那么 S 的参数类型必须是 T 中参数的超类型。

// 严格函数类检查
type S = (a: number) => number;
type T = (b: 0 | 1) => number; 

这里参数 ab 的超类型, S 是 T 的子类型,下图用不正确的类型验证并开启严格函数类型检查会报错。

函数返回值类型

非严格函数类型检查模式严格函数类型检查模式下,函数返回值类型与函数类型始终是协变关系。若函数类型 S 是函数类型 T 的子类型,那么 S 的返回值类型必须是 T 返回值类型的子类型。

type S = (a: number) => 0;
type T = (b: 0 | 1) => number; 

这里参数 a 是参数 b 的超类型,满足 S 是 T 子类型的函数参数数量及类型条件要求,同时 S 的返回值类型是字面量 0number 类型的子类型。若将 S、T 的返回值类型反过来,则破坏了协变关系,导致类型报错,见下图。

函数重载

若 S 是 T 的子类型,并且 T 存在函数重载,则 T 的每一个函数重载都能在 S 的函数重载中找到对应的子类型;S 中找到的子类型可以重复使用,就是说 S 的函数重载签名数量可以少于 T 的。

interface S {
    (a: string): 'a'
    (a: string, b: boolean): string
}
interface T {
    (a: string, b?: boolean): string
} 

上面代码 S 是 T 的子类型的分析过程如下:

1.T 上具有一个函数重载 (a: string, b?: boolean): string (简称重载 T1),于是去 S 上找对应的函数重载
2.S 上的函数重载 (a: string): 'a' (简称重载 S1) 和上一步的函数重载按照函数类型的子类型兼容性规则判断兼容性
3.T1 的参数数量为 2,S1 的参数数量为 1,满足数量要求
4.S1 的参数类型是 T1 的参数类型的子类型(都是 string 类型,具有自反性),满足参数类型要求
5.S1 返回值类型是 T1 返回值类型的子类型(字符串字面量是 string 类型的子类型),满足返回值类型要求。若修改 S1 的返回值类型将导致不兼容,产生下图报错
6.这里 TypeScript 编译器根据 S 的函数重载顺序判断 T1 所有重载在找到了对应重载,不再继续往下判断 S 上的另一个重载(但是我们还是继续分析 S 上的另一个重载)
7.S 上存在另一个重载 (a: string, b: boolean): string (简称 S2),与 T1 按照函数类型的子类型兼容性规则进行判断
8.T1 的参数数量为 2,S2 的参数数量为 2,满足数量要求
9.T1、S2 的参数 a 满足类型要求,T1 的参数 b 类型为 boolean | undefined ,S2 的参数 b 类型为 boolean 不满足子类型要求
10.重载 S2 与重载 T1 不兼容

子类型函数重载返回值不兼容导致报错

子类型函数重载参数类型不兼容导致报错

对象类型

对象类型是有零个或多个基本类型、函数类型成员组成,比较对象类型的子类型关系时需要分别比较每一个类型成员子类型关系。

结构化子类型

TypeScript 中对象类型间的子类型关系取决于对象的结构,对象类型的名称不影响其子类型关系,这一特性叫做[[结构化子类型]],也可以通俗地理解为[[鸭式辨型]]。

这里类型 S、T 名称不同,但是具有相同成员类型,S 是 T 的子类型,同时 T 也是 S 的子类型,即S、T 满足子类型关系。

属性成员类型

  • 若对象类型 S 是对象类型 T 的子类型,则对于 T 中的每一个属性成员 M,都能够在 S 中找到一个同名的属性 N,满足 N 是 M 的子类型。也就是说 S 必须包含 T 中的所有属性成员,T 的属性成员个数不能多于 S 的。该关系也可以简单记为:协变
  • 另外,对象类型 T 中的必选属性成员在 S 中也必须是必选属性成员。

这里 S 包含 T 中所有属性成员,满足子类型兼容性

这里 T 的必选属性 y 在 S 中不是必选属性,不满足子类型兼容性

调用签名与构造签名

  • 若对象类型 S 是对象类型 T 的子类型,则对象类型 T 中每一个调用签名 M 都能在对象类型 S 中找到一个调用签名 N,满足 N 是 M 的子类型。该关系也可以简单记为:协变
  • 构造签名的子类型判断规则和调用签名相同,也就是说对象类型的子类型必须包含其超类型的调用签名或构造签名,同时调用签名或构造签名还要满足子类型要求。

字符串索引签名

若对象类型 S 是对象类型 T 的子类型,如果 T 中存在字符串索引签名(对象类型只能存在一个字符串签名),则 S 中也应该存在字符串索引签名,并且 S 中的字符串索引签名是 T 中字符串索引签名的子类型。该关系也可以简单记为:协变

数值索引签名

若对象类型 S 是对象类型 T 的子类型,如果 T 中存在数值索引签名(对象类型只能存在一个数值索引签名),则 S 中也应该存在数值索引签名或字符串索引签名,并且是 T 中数值索引签名的子类型。该关系也可以简单记为:协变

若对象类型、接口中同时存在数字索引签名和字符串索引签名时,数值索引签名必须能够赋值给字符串索引签名,因为在 JavaScript 中,对象的属性名只能为字符串或 Symbol,数组的数值索引最终也会被转为字符串索引。因此,数值索引签名表示的集合是字符串索引签名表示的集合的子集。

所以,与上面字符串索引签名的区别在于,与 T 中对应的可以是数值索引签名或字符串索引签名,相当于把子类型索引签名将类型放宽了,可以和函数类型参数类型的放宽类比。

类实例类型

  • 在判断两个类之前的子类型关系时,仅检查类的实例成员类型,不检查类的静态成员类型、构造函数类型。

这里类 S、T 的构造函数类型不同,类 S 的实例成员类型满足子类型关系(根据对象类型的属性成员类型判规则检查子类型关系),所以 S 是 T 的子类型。

这里修改类 S 的属性 x 的类型,导致类型 S 的实例成员类型子类型关系检查不通过,S 不是 T 的子类型。

  • 对于类的私有成员、受保护成员,检查子类型关系时要求其来自于同一个类,即两个类必须存在集成关系。

这里类 S 的成员 x 位受保护成员,S 并非继承自 T,故 S 不是 T 的子类型。

这里 T1 具有私有成员 x,同时 T1 继承自 T,故 T1 是 T 的子类型。

泛型

泛型可以理解为:类似于函数形参在被函数调用时传入的实参替换,泛型分为泛型对象类型、泛型函数类型。

泛型对象类型

泛型对象类型包含:泛型接口、泛型类、泛型类型别名,TypeScript 在检查其子类型关系时泛型的类型参数不参与,泛型对象的结果对象类型参与。

interface Event<T> {}
type T = Event<string>
type S = Event<number> 

这里 Event<T> 为泛型接口,其结果类型为[[空对象类型字面量]] {} ,S、T 的类型为泛型接口的结果类型,也为[[空对象类型字面量]] {} 。根据上面对象类型分析可知,两个空对象类型字面量互相是子类型,即:S 是 T 的子类型,T 也是 S 的子类型。

interface Event<T> {
 data: T
}
type T = Event<string>
type S = Event<number> 

这里类型别名 T 的类型为泛型接口 Event<string> 的结果类型,为 { data: string } 类型,称作类型 R1;类型别名 S 的类型为泛型接口 Event<number> 的结果类型,为 { data: number } 类型,称作类型 R2。根据对象类型的子类型关系判断规则可知,R2 上的 number 类型的属性 data 不是 R1 上 string 类型的属性 data 的子类型,故 S 不是 T 的子类型;同理,T 也不是 S 的子类型。

上述代码在 TypeScript 中验证见下图:

泛型函数类型

与函数参数类型检查相似,编译器在检查泛型函数类型是也有非严格泛型函数类型检查严格泛型函数类型检查两种检查模式,可以通过 --noStrictGenericChecks 编译选项来配置。

非严格泛型函数类型检查 编译器将所有泛型参数类型替换为 any 类型,然后再按照函数类型的子类型关系检查规则来检查子类型兼容性。

type T = <U, V>(a: U, b: V) => [U, V]
type S = <W>(a: W, b: W) => [W, W] 

这段代码,在非严格泛型函数类型检查模式下,编译器检查步骤如下:

1.替换所有泛型参数为 any 类型

type T = (a: any, b: any) => [any, any]
type S = (a: any, b: any) => [any, any] 

将泛型参数替换成了具体的类型 any,所以不再是泛型函数签名,去掉泛型参数并成为了普通函数签名。 2. 根据函数类型的子类型判断规则依次检查两个函数类型的参数数量、参数类型、返回值类型,这里两个函数签名类型完全相同符合函数类型的子类型关系要求 3. 得出检查结果:S 是 T 子类型,T 也是 S 的子类型

严格泛型函数类型检查 在严格泛型函数类型检查模式下,编译器不再使用 any 类型替换泛型参数,而是先通过类型推断来统一两个泛型函数的类型参数,再确定两者的子类型关系。

type T = <U, V>(a: U, b: V) => [U, V]
type S = <W>(a: W, b: W) => [W, W] 

假设要检查 T 是否是 S 的子类型,编译器处理步骤如下:

1.尝试使用 S 的类型来推断 T 的类型,得出 T 的参数类型 U、V 都为 W 类型
2.根据推断将 T 的泛型实例化

type T = <W>(a: W, b: W) => [W, W]
type S = <W>(a: W, b: W) => [W, W] 

2.比较泛型实例化后两者的子类型关系,这里两者的类型完全相同
3.得出结论:T 是 S 的子类型

那这里是否能确定 S 是 T 的子类型呢?答案是不能的,因为要确定 S 是 T 的子类型需要反过来使用 T 的类型来推断 S 的类型,具体过程见下面分析。

要检查 S 是否是 T 的子类型,编译器处理步骤如下:

1.尝试使用 T 的类型来推断 S 的类型,得出 S 的参数类型 W 为联合类型 U | V
2.根据推断将 S 的泛型实例化

type T = <U, V>(a: U, b: V) => [U, V]
type S = <U, V>(a: U | V, b: U | V) => [U | V, U | V] 

2.比较泛型实例化后两者的子类型关系,这里需要判断类型 S 是否是类型 T 的子类型
3.根据函数类型的子类型判断规则依次检查函数参数数量、参数类型、返回值类型,参数数量是满足要求的,但是参数类型、返回值类型不满足函数子类型要求

这里判断 U | V 是否是 U 的超类型(非严格函数类型检查模式)、或是否具有子类型关系(严格函数类型检查模式)时,需要知道联合类型的子类型判断规则,见下文说明。

3.得出结论:S 不是 T 的子类型

这里 S 不是 T 的子类型验证见下图:

联合类型

联合类型由若干成员类型构成,检查联合类型时需要考虑各个成员类型的子类型关系。检查规则为:若 S 的所有成员类型都是类型 T 的子类型,则 S 是 T 的子类型,也是[[联合类型]]中交集的概念。

这里 S 的成员类型 01 都是 T 的子类型,所以 S 也是 T 的子类型。

type T = number
type S = 0 | 1 

这里 S 的成员类型 string 不是 T 的子类型,所以 S 不是 T 的子类型。

这里 S 和 T 都是联合类型,S 的成员类型 1a 都是 number | string 的子类型,所以 S 是 T 的子类型。

type T = number | string
type S = 1 | 'a' 

联合类型 number | string 表示类型是 number string 类型之一,而字面量类型 1number 类型的子类型,所以 1number | string 的子类型,anumber | string 的子类型的推导同样。

交叉类型

交叉类型由若干成员类型构成,检查交叉类型时需要考虑各个成员类型的子类型关系。检查规则为:若 S 至少有一个成员类型是类型 T 的子类型,则 S 是 T 的子类型,也是[[交叉类型]]中并集的概念。

这里 S 的成员类型 number 是 T 的子类型,所以 S 是 T 的子类型。

type T = number
type S = number & string 

这里 S 的所有成员类型都不是 T 的子类型,所以 S 不是 T 的子类型。

这里 S 的成员类型都不是 T 的子类型,但是 S 却是 T 的子类型,因为 S 的结果类型为 never 类型,而 never 为[[尾端类型]]是所有类型的子类型。

type T = number
type S = 'a' & 'b' 

S 的结果类型为 never 类型。

一些疑难点

函数的子类型兼容性描述的是函数,而多态描述的是对象,两者相同?

在上文中函数类型提到使用子类型替换超类型这一原则就是多态,为何函数的子类型关系也能用多态来解释?

据面向对象程序设计中[[里氏替换原则]]描述,程序中任何使用了超(父)类型的地方都可以使用其子类型进行替换。TypeScript 中的子类型兼容性体现的就是这一原则,而这也正是多态。

解答如下:

在 js 中函数也是对象,TypeScript 中将其类型描述为具有调用签名的对象,详细可见[[函数签名]]。所以,函数的子类型关系也属于对象类型的子类型关系,也算是多态。

最后

为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

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

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

相关文章

艾美捷寡聚脱氧核苷酸CpG ODN:ODN 1668

艾美捷CpG ODN&#xff1a;ODN 1668&#xff1a;具有硫代磷酸酯骨架的CpG寡脱氧核苷酸&#xff08;B型&#xff09;。小鼠TLR9&#xff08;Toll样受体9&#xff09;的特异性配体。 艾美捷Enzo CpG ODN&#xff1a;ODN 1668详细信息&#xff1a; 序列&#xff1a;5-tccatgtgtcc…

Akka 学习(九)Akka Cluster

参考文章 Gitter Chat&#xff0c;Akka 在线交流平台Akka Forums&#xff0c;Akka 论坛Akka in GitHub&#xff0c;Akka 开源项目仓库Akka Official Website&#xff0c;Akka 官网Akka Java API&#xff0c;Akka 应用程序编程接口《Akka入门与实践》 [加]Jason Goodwin&#x…

AI创业时代!这9个方向有钱途;AIGC再添霸榜应用Lensa;美团SemEval2022冠军方法分享;医学图像处理工具箱… | ShowMeAI资讯日报

&#x1f440;日报合辑 | &#x1f3a1;AI应用与工具大全 | &#x1f514;公众号资料下载 | &#x1f369;韩信子 &#x1f4e2; 2023年9个有前途的 AI 创业方向 https://www.aiplusinfo.com/blog/9-promising-artificial-intelligence-startup-ideas-for-2023/ 2022年&#x…

央视广告怎么做,做好央视广告的关键点有哪些

企业品牌打算投放央视广告&#xff0c;首先想到的是如何做才更有效果&#xff0c;往往本身就比较困惑。公司应该投入多少资金&#xff1f;应选用何种广告投放方案&#xff1f;有时公司内部的观点也是不同的。尤其是面对广告公司提供的众多方案&#xff0c;让负责人看着都头疼&a…

Tos-GPR-AMC,117961-27-0,标记肽Tos-GPR-7-氨基-4-甲基香豆素

胰蛋白酶和凝血酶的荧光底物 Tos-GPR-AMC, fluorogenic substrate for trypsin and thrombin. Kinetic data for Thrombin: kcat 180, Km 310 uM. 编号: 190611中文名称: 标记肽Tos-GPR-7-氨基-4-甲基香豆素英文名: Tos-Gly-Pro-Arg-AMCCAS号: 117961-27-0单字母: Tos-GPR-AM…

代码随想录训练营第6天

专题&#xff1a;哈希表 题目&#xff1a;有效的字母异位词 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 例如 输入: s "anagram", t "nagaram" 输出: true 说明: 你可以假设字符串只包含小写字母。 题目理解&…

av_interleaved_write_frame():Broken pipe

使用FFmpeg命令rtsp推流到Docker中MediaServer服务器&#xff0c;15秒timeout一到就会打印错误信息&#xff0c;如下图&#xff1a; av_interleaved_write_frame():Broken pipe 命令中指定rtsp传输方式为tcp&#xff08;-rtsp_transport tcp &#xff09;&#xff0c;虽然不会…

第二证券|锂电起火风险完美解决?美科学家研发新型超高盐度电解质

现在&#xff0c;研讨人员正致力于以各种方式改善电池技能&#xff0c;其中最重要的是处理可燃性问题。 美国科学家们近期研宣布了一种新的电解质配方&#xff0c;就能够以一种十分有出路的方式处理这一问题&#xff0c;依托额定的盐含量来防止有问题发生的化学反应。 众所周知…

[附源码]JAVA毕业设计疫情期间高校师生外出请假管理系统录屏(系统+LW)

[附源码]JAVA毕业设计疫情期间高校师生外出请假管理系统录屏&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&am…

婚礼策划预约小程序开发,拓展客户人群

婚礼对于所有人来说都是非常重要的一件事&#xff0c;是人生中最值得回忆的美好场景。在经济水平的不断提高下&#xff0c;人们在结婚之前都要找专业团队对婚礼进行策划&#xff0c;让婚礼更加完美&#xff0c;正因如此&#xff0c;婚礼策划行业得到了迅速发展。但是从众多策划…

Zabbix-proxy安装(zabbix 6.0LTS)

关于zabbix 系列的博文我已经很久没有更新了&#xff0c;主要原因是最近太忙了。如果读者诸君有认真阅读我前面的文章并亲自实践过的话&#xff0c;相信你肯定会大有收获的。到目前为止zabbix 的基础知识在我的博文中基本已经全都涉及到了&#xff0c;至于如何运用&#xff0c;…

赛狐ERP11月新功能汇总 | 13大场景,超90项功能升级

赛狐ERP月度《产品升级》栏目来啦&#xff01; 11月我们加紧迭代了运营、进销存、财务、报表等多个维度的功能内容&#xff0c;聚焦卖家关注的需求要点&#xff0c;进行了13大业务场景&#xff0c;超90项功能升级。赛狐ERP将不断优化产品细节&#xff0c;提升卖家体验&#xf…

CH36X系列接口芯片Linux系统开发库说明

一、概述 ch36x_lib库是Linux系统下用于开发CH36X系列设备CH365/CH367/CH368等芯片应用的软件接口库&#xff0c;此接口库主要提供以下功能&#xff1a; 设备打开/关闭 获取芯片类型/驱动版本 获取IO基地址/Memory基地址/中断号 配置空间读写&#xff08;支持单字节/双字节…

基于java+springboot+mybatis+vue+mysql的体育场馆运营系统

项目介绍 在系统流程分析当中调查分析它是比较重要的环节&#xff0c;因为在这个系统当中它都涉及到每个环节的业务流程&#xff0c;所以从计算机毕业设计SpringBootVue选题推荐—体育场馆运营系统的设计的整体设计上要保证各个信息的正确输入和输出以及对数据储存的完整&…

linux下JDK安装

先下载离线安装包&#xff0c;我将安装包直接放到/root/softPackages目录下&#xff08;需要安装包可私聊&#xff09; 1.卸载已有OpenJDK(如果有) 查找已经安装的OpenJDK包 rpm -qa | grep java 如果有查出结果&#xff0c;直接yum -y remove卸载即可&#xff1a; 例如&a…

MobileNetV1作为CenterNet的Backbone尝试

1、CenterNet对于Backbone的要求是&#xff1a; 输入为512*512&#xff0c;输出为&#xff1a;heatmap&#xff08;128*128*C&#xff09;、wh&#xff08;128*128*2&#xff09;、reg&#xff08;128*128*2&#xff09;。 2、原生的MobileNet V1输出&#xff1a; 最后一层通…

JSP:使用 sitemesh/decorator装饰器装饰jsp页面(原理及详细配置)

摘要&#xff1a;首先这个Decorator解释一下这个单词&#xff1a;“装饰器”&#xff0c;我觉得其实可以这样理解&#xff0c;他就像我们用到的Frame&#xff0c;他把每个页面共有的东西提炼了出来&#xff0c;也可能我们也会用各种各样的include标签&#xff0c;将我们的常用页…

关于使用C语言实现负值和非负值进行左右划分,奇数和偶数进行左右划分

目录 1.负值和非负值左右划分 2.偶数和奇数的划分 数据结构之折半查找&#xff08;递归和非递归&#xff09;&#xff0c;插值查找和斐波那契查找 归并排序求逆序对&#xff08;C语言&#xff09; 1.负值和非负值左右划分 要求&#xff1a;使用尽可能少的时间将一组连续的序…

BlockRender for 3DMAX一键区域渲染插件使用教程

BlockRender一键区域渲染插件&#xff0c;自定义区域渲染并在视口中直接查看结果&#xff0c;并可以自动或手动对渲染的图像进行保存。 这个小插件的亮点就在于它能对渲染的图像进行保存&#xff0c;如果没有保存图像的功能&#xff0c;显然这个插件没有任何存在的意义。有了保…

分布式操作系统 - 6.分布式同步控制

文章目录1.物理时钟同步1.1 时钟同步问题1.2 时钟同步算法1.3 网络时间协议&#xff08;1&#xff09;Christian算法&#xff08;2&#xff09;考虑的问题&#xff08;3&#xff09;Berkeley算法-集中式方法&#xff08;4&#xff09;平均值算法-非集中式方法&#xff08;5&…