第08章 深度掌握泛型函数、泛型函数重载【无比重要的深度技能】
4-1 【泛型函数】—泛型函数+快速排序算法【比冒泡稍复杂】 【透彻理解泛型函数的好处】【共两节】
泛型函数课程安排:
- 大致讲解快速排序算法
- 编写字符串排序、整数排序函数
- 泛型函数实现字符串排序、整数排序【讲解泛型函数带来的好处】
(1)快速排序算法【 快速排序算法不属于本课程内容,老师会简单讲下,同学们有个基本了解即可, 如不能完全理解,先用即可,重心一定要先放在泛型函数和接下来的泛型函数重载上】
(2)编写字符串排序丶整数排序函数代码:
// 快速排序算法思路:
// 1.先从数列中取出一个数作为基准数。
// 2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
// 3.再对左右区间重复第二步,直到各区间只有一个数。
function quickSort(arr: Array<any>): any {
if (arr.length < 2) {
return arr
}
const left: Array<any> = [];
const right: Array<any> = [];
const mid = arr.splice(Math.floor(arr.length / 2), 1)[0];
for (let i = 0; i < arr.length; i++) {
if (arr[i] < mid) {
left.push(arr[i]);
} else {
right.push(arr[i])
}
}
return quickSort(left).concat(mid, quickSort(right))
}
const chineseArr = [3, 1, 8, 9, 20, 15, 2, 7, 13, 11, 19, 18, 5, 6, 17, 4];
const chineseArrSort = quickSort(chineseArr)
console.log("chineseArrSort:", chineseArrSort)
const strArr: Array<string> = ["cba", "kkdf", "ndf", "bcdf", "dfd", "cdf"]
const strArrSort = quickSort(strArr)
console.log("strArrSort:", strArrSort)
(3) 泛型函数实现字符串排序,整数排序【讲解泛型函数带来的好处】泛型类的格式:
// 泛型函数实现的快速排序方法
function quickSort(arr) {
if (arr.length < 2) {
return arr;
}
const left = [];
const right = [];
const mid = arr.splice(Math.floor(arr.length / 2), 1)[0];
;
for (let i = 0; i < arr.length; i++) {
if (arr[i] < mid) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat(mid, quickSort(right));
}
4-2 【泛型函数重载】泛型函数重载准备【经典复杂排序器】—自排序、多种数据类型、中文排序【共三节】
泛型函数重载课程安排:
- 中文排序
- 字符串自排序
- 中文+英文、数字数组排序
- 中文+英文、数字数组 + 数组内部字符串自排序
- 字符串自排序 +中文+英文、数字数组+数组内部字符串自排序
1. 中文排序
2. 字符串自排序
function quickSort<T>(arr: Array<T>): T[] {
if (arr.length < 2) {
return arr
}
const left: Array<T> = [];
const right: Array<T> = [];
const mid = arr.splice(Math.floor(arr.length / 2), 1)[0];
for (let i = 0; i < arr.length; i++) {
if (arr[i] < mid) {
left.push(arr[i]);
} else {
right.push(arr[i])
}
}
return quickSort(left).concat(mid, quickSort(right))
}
// 字符串自排序
function strSelfSort(str: string): string {
// (1) 字符串拆分成数组
const strArray = str.split('');
// (2) 数组进行使用快速排序算法来排序
const strSortArray = quickSort(strArray);
// (3) 重新把排好序的数组连接成一个字符串返回
return strSortArray.join('');
}
3. 中文+英文、数字数组排序
const pattern1 = /[\u4E00-\u9FA5]+/g;
// 3泛型函数重载准备
// (1). 中文排序
// (2). 字符串自排序
function quickSort<T>(arr: Array<T>): Array<T> {
if (arr.length < 2) {
return arr
}
const left: Array<T> = [];
const right: Array<T> = [];
const mid = arr.splice(Math.floor(arr.length / 2), 1)[0];
console.log("mid:", mid)
for (let i = 0; i < arr.length; i++) {
if (arr[i] < mid) {
left.push(arr[i]);
} else {
right.push(arr[i])
}
}
return quickSort(left).concat(mid, quickSort(right))
}
// localeCompare
const str = "cbaimcnd"
// let strArrSort = quickSort(str)
// (2). 字符串自排序
function strSelfSort(str: string): string {
// (1) 字符串拆分成数组
const strArray = str.split('');
// (2) 数组进行使用快速排序算法来排序
const strSortArray = quickSort(strArray);
// (3) 重新把排好序的数组连接成一个字符串返回
return strSortArray.join('');
}
console.log(strSelfSort(str));
export {}
4. 中文+英文、数字数组 + 数组内部字符串自排序
5. 字符串自排序 +中文+英文、数字数组 + 数组内部字符串自排序
4-3 【泛型函数重载】泛型函数重载重构自排序丶多种数据类型丶中文排序 【彻底理解泛型函数重载带来的优势】
- 重载签名只是外部使用的时候看得见,重载签名上无需添加默认值,最终使用的是实现签名的默认值。
- 实现签名不参与调用,外部使用的时候看不见
4-4 【泛型函数重载】 结合 Vue3 源码深度掌握泛型函数重载
export function ref<T extends object>(value: T): ToRef<T>
export function ref<T>(value: T): Ref<UnwrapRef<T>>
export function ref<T = any>(): Ref<T | undefined>
export function ref(value?: unknown) {
return createRef(value)
}
4-5 泛型工厂函数——深入泛型工厂函数+泛型工厂函数的深度应用
泛型工厂函数课程安排
准备 相关定义+【函数类型复习】
理解泛型工厂函数类型
通用函数类型-->工厂函数类型-->泛型工厂函数类型
构建泛型工厂函数
泛型工厂函数深度应用 —原型+泛型工厂函数联合使用
工厂函数类型定义:代表任意一个类的构造函数【等价JS的构造函数】的函数类型。
泛型工厂函数定义:一个可以创建任意类对象的通用泛型函数
泛型工厂函数应用场景: 使用场景1:在一些不方便或者没有办法直接 new 类名()格式来创建对象,例如:后面讲解的装饰器中就多次用到。 使用场景2:在一些项目测试或者调试中简化代码使用。
通过工厂函数的学习,既可以加深对泛型函数的理解;同时也可以扩大技术视野,提升代码整合能力;还为一些优秀前端技术打下更雄厚的技术根基【例如:为理解装饰器中各种复杂代码打下技术根基】。
type promiseFuncType = (...args: any[]) => any
class ShopCartService {
public productname: string;
public count: number;
constructor() {
}
addShopCart() {
console.log("增加商品:", this.productname + ":数量:", this.count)
}
}
class Promise {
constructor() {
return this
}
static Promise(promiseFunc2: promiseFuncType): void {
promiseFunc2("sucess", "fail");
//return this
}
buy() {
console.log("buy")
}
static ShowResult(promiseFunc3: promiseFuncType, errorCode: string) { }
}
let shopCartService = new ShopCartService()
shopCartService.productname = "牙膏"
shopCartService.count = 70
// 泛型工厂函数类型constructorFuncType
type constructorFuncType<T = any> = new (...args: any[]) => T
// 构建泛型工厂函数写法1:参数类型为type定义的构造函数类型
function createInstanceFactory<T>(promiseFunc2: constructorFuncType<T>) {
return new promiseFunc2()
}
let promise = createInstanceFactory<Promise>(Promise);
promise.buy()
let shopCartService2 = createInstanceFactory<ShopCartService>(ShopCartService);
shopCartService2.addShopCart();
// 构建泛型工厂函数写法2:参数类型为接口式的构造函数类型
interface constructorFuncinterface<T = any> {
new(...args: any[]): T
}
//function createInstanceFactory2<T>
//(promiseFunc2: { new(...args: any[]): T }): T {
function createInstanceFactory2<T>
(promiseFunc2: constructorFuncinterface<T>): T {
return new promiseFunc2()
}
let promise2 = createInstanceFactory<Promise>(Promise);
promise2.buy()
// 构建泛型工厂函数写法3:参数类型为直接写出来的构造函数类型
function createInstanceFactory3<T>
(promiseFunc2: new (...args: any[]) => T): T {
return new promiseFunc2()
}
let promise3 = createInstanceFactory<Promise>(Promise);
promise3.buy()
export { }
4-6 【 TS 交叉类型】理解交叉类型和深入分析交叉类型的应用场景
4-6 课程安排
1 交叉类型定义丶和联合类型区别
2 交叉类型的应用场景概述
3 举例展现如何使用交叉类型,体验交叉类型好处。
定义:将多个类型合并【多个类型属性和方法的并集】成的类型就是交叉类型。
和联合类型区别:
赋值区别:
对于对象类型合成的交叉类型是多个类型属性和方法的合并后的类型,属于多个类型的并集,必须是两个类型的全部属性和方法才能赋值给交叉类型变量。【可选属性和方法除外】
对于对象类型合成的联合类型变量可以接受联合类型中任意一种数据类型全部属性和方法,也可以是两个类型的全部属性和全部方法【可选属性和方法除外】,也可以是一种数据类型全部属性和方法+其他类型的某个属性和某个方法。
获取属性和方法区别:
交叉类型变量可以获取两个类型的任意属性和任意方法,而联合类型的变量只能获取两个类型的共同属性和方法【交集属性和交集方法】
交叉类型应用场景
通常用于多个对象合并的场景。比如:我们把用户信息,用户角色信息合并成一个对象然后输出。当然后端可以通过连接查询的 SQL 语句来完成到前端的多对象输出,但大多需要表的外键来支持,比如用户和角色就需要角色表有用户外键,对于现实生活中有必须关联在一起的实体【比如商品和库存信息】一般建议数据表用外键来支持前端多个对象的合并输出,虽然影响了效率,但也保证了表的数据合理性和完整性。
但如果我们临时需要随机把两个根本没有外键关联的数据表取出来的对象合并在一起输出,比如用户信息和日志信息,商品信息和日志信息,订单信息和日志信息,我们就可以用交叉类型来完成。因为我们不可能为了这个临时的对象合并需求把所有的这些表都建立起外键,须知外键太多不仅增加了数据表维护的负担,而且也有较大的影响了表操作效率。
可能没有数据表基础的同学不太理解,效率低到哪了呢?比如添加一个用户,必然要添加日志 id 【外键】信息,那就要判断这个日志 id 是否在日志表中是否存在,这就是需要时间,当用户访问量大时,某个时刻都要完成添加操作【高并发操作】,这个外键时间检查损耗就比较严重,尽管我们通过各种 SQL 优化来提高 SQL 效率, 【就像一个拖着一辆大卡车【 A 表】的大货车【 B 表】,无论如何调优,也不比空跑的大货车快】
**所以综上所述:交叉类型的应用场景1:可应用这些没有关联的对象合并上,因为这样会极大的方便前端页面的输出。合并如同打包,比单一的一个一个的筛选输出要方便很多,整体感要好很多。 **
交叉类型的应用场景2: 一些 UI 库底层如果用到多个密切连接在一起的关联类型时,可以使用交叉类型来合并输出。
4-7 【泛型函数+交叉类型+类型断言+枚举】 深度理解泛型函数+交叉类型综合应用
泛型函数+ TS 交叉类型 代码:
function cross<T extends object, U extends object>(objOne: T, objTwo: U): T & U {
let obj = {}
let combine = obj as T & U
Object.keys(objOne).forEach((key) => {
combine[key] = objOne[key]
})
Object.keys(objTwo).forEach((key) => {
if (!combine.hasOwnProperty(key)) {
combine[key] = objTwo[key]
}
})
return combine;
}
4-7后 彩蛋:【泛型函数重载+交叉类型+类型断言】 再度加深掌握交叉类型的综合应用【更上一层楼】
function cross<T extends object, U extends object>(objOne: T, objTwo: U): T & U
function cross<T extends object, U extends object, V extends object>
(objOne: T, objTwo: U, objThree: V): T & U & V
function cross<T extends object, U extends object, V extends object>
(objOne: T, objTwo: U, objThree?: V) {
let obj = {}
let combine = obj as T & U
Object.keys(objOne).forEach((key) => {
combine[key] = objOne[key]
})
Object.keys(objTwo).forEach((key) => {
if (!combine.hasOwnProperty(key)) {
combine[key] = objTwo[key]
}
})
if (objThree) {//如果有第三个对象传递进来实现交叉
//let obj = {}
//let combine2 = obj as T & U & V
//let combine2=combine as T & U & V
let combine2 = combine as typeof combine & V
Object.keys(objThree).forEach((key) => {
if (!combine2.hasOwnProperty(key)) {
combine2[key] = objThree[key]
}
})
return combine2// 三个对象交叉结果
}
return combine;// 两个对象交叉结果
}