Typescript学习笔记
什么是Typescript
TypeScript是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码;TypeScript适合用来编写基于node的大型应用;ts的静态语言特性,能帮助我们在开发时候就发现自己语法的错误,而非像其他js在运行时才发现;ts的类型定义,让代码更加规范,在调用一些方法和api时,能准确的得到方法参数和返回值提示,减少了以往js需要api文档才能开发的问题;
Typescript 的数据类型
基本数据类型
布尔类型(boolean); 数字类型(number); 字符串类型(string); 数组类型(array); 元组类型(tuple); 枚举类型(enum); 任意值类型(any); null和undefined; void类型; never类型;
常用类型(与java的定义差不多)
//最常用的类型
let bool:boolean = true;
let str : String = "字符串";
let num : number = 1.2;
//两种数组的定义方式
let strArr : string[] = ["1","23","123"];
let numArr : Array<Number> = [1,2,3];
Typescript 的强类型判断非常严格,当试图为上文的 str赋值 数字类型时,会报错,体现了ts的静态语言特性,强类型判断
//元组(Tuple)可以理解为一个长度,每一项元素类型都确定的数组
let peopleArr: [string, string, number] = ['Dylan', 'male', 23] //数组中元素的类型和长度都是固定的,与定义的类型和长度一样
//混合类型的数组
let arr:(number|string)[] = [12,'aa','a'];//定义数组类型时,使用 | 连接(union type联合类型),这数据可放入多种类型
any类型及其与object的区别
any任意值类型,在强调类型的ts中,ts对象可以让被赋予如何类型,也可以从any中去任意属性和方法(即使不存在的属性和方法)
let testObj : any = {};
testObj=1;
testObj="aaa";
testObj={
sayHi(){
console.log("sayHi")
}
}
console.log(`testObj不存在的属性---${testObj.aa}`)//调用不存在的属性aa,编译不报错,运行也不保存
console.log(`调用不存在的方法开始`)
testObj.sayNo();//不存在的方法
console.log(`调用不存在的方法结束`)
testObj.sayHi();
如上代码和运行结果可以看到,any修饰的变量,可以接受任意类型,可以调用不存在的属性,但是不能调用不存在的方法;如上,调用不存在的方法,编译不报错,但是运行到 “调用不存在的方法开始`”这个log后,程序开始循环无法正常的往下走;any 类似原生的js对象,ts放弃对此类修饰对象的类型判断;
any与object的区别,虽然在接受参数上两者都能接收不同类型,但是编译器,object 使用不存在的属性和方法时,object类型编译报错
any可以以赋值给任意类型(never类型除外),object则不能赋值给任意类型;any对象可以赋值给string,但是object不行
never any unkown
将三种类型一起讲,是因为 any和unkown顶部类型,never是底端集合
any和unkown 是一切类型的超级类型(supertype)任何值都能冠以类型any和unkown
never是一个空集合,任何值都不能冠以类型 never;将某个值的类型解析为 never,编辑就会报错;空集合可包含于任何非空集合,因此never 是一切其它非空类型的子集合。这就是为什么 never 被称为底端集合。
包装类型
interface
以IEMP-SXTL-BFF项目代码来演示interface功能
//在src\inits\modelData\index.ts 向外暴露的CommonResultData
/**
* 服务返回数据通用结构
*/
export interface CommonResultData {
code: number;
msg: string;
data: Record<string, any>[] | null;
}
let data :CommonResultData = {
code:1,
msg:"aaa"
}
//这种定义,要求该类型的实例的对象,必须要有 code msg data属性,以code为列,code可以 = 数字,null,underfined,但是不能没有code属性
//如果偏偏要让 code 可有可无,则可以 code?: number; 使用问号,则属性中没有code,编译也不会报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UNusM0NH-1612158764817)(C:\Users\tttare\AppData\Roaming\Typora\typora-user-images\image-20210104102645724.png)]
interface无法用new 实例化,class可以
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IrDWkKha-1612158764818)(./img\2020-12-31_153131.png)]
如上图,我们在使用interface修饰的类型时,能很清晰的看到这个类型所需要的属性,这也是ts相比传统js的优势,能让我们在没有api文档的情况下,知道使用的类型中的属性,属性类型,方法,方法参数等信息,这也是ts解决的痛点之一;
let comdata : CommonResultData = {
code:1001,
msg:"aass",
data:null
};//给CommonResultData类型赋值时,CommonResultData类型的三个属性,必须全都要定义,不能是underfine,类型也不能不同
interface和java的接口还是很像的,interface能继承interface,interface不能有方法体,不能用private修饰方法
ts建议给interface的方法确定返回值
class
在IEMP-SXTL-BFF项目中,class修饰的类型承担更大的作用;class中有大量的方法和注解,主要用来编写业务逻辑
//定义一个oprate类
class Oprate{
public sayHi(){
console.log('public say hi')
}
private sayHello(){
console.log('private say hello')
}
public static sayHalo(){
console.log('static say halo')
}
}
由上图可知,class的实例能使用被public修改的方法,不能使用被private或static修饰的方法;static修改的方法,直接用类型调用
private 和 public都与java的类似,只是static修饰的方法,类的实例不能使用,与java不相同
extends ,implements,多态
interface不能被class继承
interface能继承interface,interface不能有private修饰方法,不能有方法体;
class实现interface必须也定义interface属性,实现interface的方法
class继承class,能使用父class的方法及属性
ts也有多态,interface接收实现类的实例,class接收子类的实例
union(并集) 和 intersection(交集)
定义几个class对象 用来测试交集和并集
interface OpInterface {
data:number;
sayToday():number;
}
class Oprate implements OpInterface{
data:number;
public sayToday():number{
return 12;
}
}
class OprateSon extends Oprate{
public saySon(){
console.log("son class")
}
}
class Dev {
code:number;
data:number;
public saySon(){
console.log("son class")
}
public sayDev(){
console.log("dev class")
}
}
class Mix {
code:number;
//下面的方法和属性在 生成的并集中也不存在,但是new Mix可以赋值给并集type
t1 : string='str';
sayNo(){
}
}
class And {
data : number;
public saySon(){
console.log("son class")
}
}
并集,ts以 | 符号表示
//基本数据类型并集
type mixBasic = number|String;
let a2 : mixBasic = 1;
let a3 : mixBasic = 'str';
//包装类型并集
type mix = Dev | OprateSon;
let devObj : mix = new Dev();
let opObj : mix = new OprateSon();
交集,ts以 & 符号表示
//基本数据类型
//mixBasic = number|String;她与string的交集 便是string
type andBasic = mixBasic&string;
let and1 : andBasic = "aaa";
//包装类型并集
type mixOne = Dev | OprateSon;
type mixTwo = Dev | Oprate;
type and = mixOne & mixTwo;
let obj1 : and = new Dev();
let obj2 : and = new OprateSon();//mixOne 和 mixTwo 的交集应该只有 Dev类型,为啥可以赋值 OprateSon
//因为 OprateSon 继承了 Oprate,所以ts继承的特性也体现出来了 OprateSon&Oprate 产生的交集是 OprateSon
枚举
枚举定义方式
enum OprateEnum{
START,END
}
console.log("start..."+OprateEnum.START)
console.log("end..."+OprateEnum.END)
运行结果
可以看出,ts的枚举和java枚举有很大不同,在我们没有给START,END赋值的情况下,枚举类型默认从0 开始自增赋值
enum OprateEnumTwo{
START,SECOND=3,THREE,END
}
//将中间值 设为3 看ts为其他值赋值多少
console.log("start..."+OprateEnumTwo.START)
console.log("second..."+OprateEnumTwo.SECOND)
console.log("three..."+OprateEnumTwo.THREE)
console.log("end..."+OprateEnumTwo.END)
运行结果
可以发现,还是从0开始,但是 因为我们给SECOND赋值了3,SECOND以后的枚举值,以她为起点自增
枚举类属性不能用数字命名
与上面的数字值枚举不同,如果你给枚举赋值了字符串,那该枚举属性之后的属性,必须赋值
当我们去掉T3 T4时,查看运行结果
TS的枚举类型,要特别主要自动赋值,自增这个特点
注解
TS提供的高级工具类
- type : 用来接收ts的类型,可以接收一个新的类型,也可以其他ts类型建立别名,别名不会新建一个类型,而是创建一个新名字来引用此类型(一个别名能对应多种类型),type 就是用来接收TS类型的
//代码中临时创建一个新的类型,用type对象接受
//NewType 是代码中临时定义的对象
type NewType = {
code?:number,
name?:string
};
let typeObj : NewType = {
code:12,
name:"sdad"
};
//为已有类型创建别名
//NewCommonResultData是CommonResultData别名,实际作用和CommonResultData一样
type NewCommonResultData = CommonResultData;
let newData : NewCommonResultData = {
code:10,
msg:"aass",
data:null
};
// type接收联合类型,对应多种类型
type DataOrStr = string | CommonResultData;
let dataOrStr : DataOrStr = "aaa";
dataOrStr = {
code:10,
msg:"aass",
data:null
};
- Partial : 产生新的类型给T所有属性加上?,变为可选
type stu = {
readonly name:string,//只读,定义后此属性不能修改
sex:string,//属性 ? 代表是否可为underfined,不要? 则接受值时,必须全部使用
level:number
};
let stuObj : stu = {name:"武汉",sex:"女",level:7};
//newStr 和 stu 互不影响,newStr时基于stu新的类型
type newStr = Partial<stu>;//Partial 产生新的类型给所有属性加上?,变为可选
let student : newStr = {level:1};//编译不报错,name和sex都可以不定义
- Required : Required 与Partial正好相反,使得所有属性,必须定义
- Readonly : 给T所有属性都加上readonly 属性,使其定义后无法修改
type readOnlyStu = Readonly<newStr>;
let readOnlyS : readOnlyStu = {name:"不可能变更"};
//readOnlyS.name = "aaa";//ReadOnly产生的type不能被修改
//readOnlyS.level = 2;//所有属性都不能被修改
- Pick<T,K extends keyof T> : 从T中挑选部分属性生成新的type
type halfStu = Pick<stu,"sex"|"level">;//Pick 从stu中,拿出部分属性,产生一个新的type
let halfStuObj : halfStu = {sex:"nan",level:1};
- Omit<T,K extends keyof T>: 删除T部分属性,产生一个新的type,与Pick正好相反
type delStu = Omit<stu,"sex">;//Omit 删除部分属性,产生一个新的type,与Pick正好相反
//let delStuObj : delStu = {sex:"a"};//sex被删除,编译报错
- Extract<T, U>:作用是从 T 中提取出 U
type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T1 = Extract<string | number | (() => void), Function>; // () => void
- Exclude<T, U>:将 T 中某些属于 U 的类型移除掉。
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
- Record<T,K extends keyof T>
Record使用效果如下
type petsGroup = 'dog' | 'cat' | 'fish';
interface IPetInfo {
name:string,
age:number,
}
type pet = Record<petsGroup,IPetInfo>;
let petObj : pet = {
dog : {
name:'dog',
age:5
},
cat : {
name:'cat',
age:9
},
fish : {
name:'fish',
age:2
}
}
IEMP-SXTL-BFF项目代码解析
项目启动环境
- 安装nodejs
- 更新配置npm
- 项目部分依赖需要python和c++类库,安装python,vs_community安装c++桌面开发工具
- npm run dev 启动项目开发环境应用
Koa基于nodeJs的Web框架
服务启动入口
src\index.ts 是整个服务器程序的启动入口
const app = await appInit.init();//调用src\app.ts的init方法,主要返回Koa实例
// 获取Koaservice运行端口号
const serverPort = normalizePort(String(commonCfg.bffConfig.port) || '3000');//端口配置于src\config
//nodeJs创建一个服务器的代码
const server = http.createServer(app.callback());
//事件掩饰设置 0 立即执行
server.setTimeout(0);
//绑定服务器端口
server.listen(serverPort);
server.on('error', onError);
server.on('listening', onListening);
koa app设置
export default {
async init(): Promise<Koa> {
//定义一个Koa 服务端实例
const app = new Koa();
// 全局变量初始化
globInit();
// 全局错误信息处理
app.use(globalError);
// 全局token校验
// app.use(checkToken(AuthService.checkToken));
// spring监控
app.use(actuator());
// 重放攻击保护
// if (commonCfg.env !== 'dev') {
// app.use(replayAttack(['swagger', 'actuator', '/system', 'example']));
// }
// 日志
app.use(koaLogger());
// 请求信息打印
app.use(requestInfo);
// 数据压缩
app.use(
koaCompress({
threshold: 2048,
flush: require('zlib').constants.Z_SYNC_FLUSH,
}),
);
// 格式化返回值
app.use(resFormatter);
// 初始化框架
initFrame(
app,
{},
{
authorizationChecker,
},
);
// eureka注册
if (commonCfg.registerToEureka) {
eureka.start(async () => {
await initData.init();
});
} else {
await initData.init();
}
return app;
},
};
主要定义了 服务器端实例
const app = new Koa();
app.use(参数:方法):只能用于注册中间件并且必须是生成器函数
app.use(function)
路由设置
src\inits\initFrame.ts中定义了koa-router及其配置,及外部访问程序的设置
const defaultRoutingControllersOptions: RoutingControllersOptions = {
//controller层包路径,于javacontroller的逻辑一致
controllers: [`${baseDir}/controllers/**/*{.js,.ts}`],
//middlewares包路劲,配置了koa-actuator 应该是永濑监控程序运行情况的
middlewares: [`${baseDir}/middlewares/**/*{.js,.ts}`],
//url前缀
routePrefix: '/bff/v1',
validation: true,
cors: true,
defaultErrorHandler: false,
};
// koa app设置路由规则
koa.use(router.routes());
编写对外提供的服务(controller + service)
如上,我们定义了一个web服务器的实例,端口,url前缀,接下来编写程序,为服务提供功能
在controllers包下定义controller类:TttareController
/**
* @Description:
* @author: 测试ts编写的controller层
* @date: 2020/01/02
*/
import { Inject } from 'typedi';
import { Body, Get, JsonController, Param, Post, Put, Authorized, Ctx } from 'routing-controllers';
import { ApiOperation, ApiTag } from '../lib/routing-controllers-openapi';
import CreateUserBody from '../definitions/CreateUserBody';
import { CustomError } from '../common/CustomError';
import UserDataManager from '../biz/example/UserDataManager';
@ApiTag('示例')
@JsonController('/tttare')
export default class TttareController {
@Inject()
private userDataInstance: UserDataManager;//ts注入
@ApiOperation('测试controller的路由和注入', '测试')
@Get('/noParam')
getTestData() {
return this.userDataInstance.getTestData();
}
@Get('/getById/:id')//rest风格 url传参
getOne(@Param('id') id: number) {
return { name: `User #${id}` };
}
//http://localhost:3001/bff/v1/tttare/getById/12
//请求返回
//{
// "name": "User #12"
//}
@ApiOperation('测试controller的路由和注入,调用参数有返回值', '测试')
@Post('/param')
getTestDataWithParam(@Ctx() ctx,//ctx是context的缩写
@Body()
queryParam: any) {
console.log(ctx);
console.log(queryParam);
return this.userDataInstance.getTestDataWithParam(queryParamList);
}
}
@Ctx() ctx是context的缩写中文一般叫成上下文,这个在所有语言里都有的名词,可以理解为上(request)下(response)沟通的环境,所以koa中把他们两都封装进了ctx对象,koa官方文档里的解释是为了调用方便,ctx.req=ctx.request,ctx.res=ctx.response,类似linux系统中的软连接?最终执行还是request和response对象
// log ctx对象如下
{ request:
{ method: 'POST',
url: '/bff/v1/tttare/param',
header:
{ 'content-type': 'application/json',
'user-agent': 'PostmanRuntime/7.24.0',
accept: '*/*',
'cache-control': 'no-cache',
'postman-token': 'aecaa0bc-108c-4a4e-8535-d36304b21b61',
host: 'localhost:3001',
'accept-encoding': 'gzip, deflate, br',
connection: 'keep-alive',
'content-length': '35' } },
response:
{ status: 404,
message: 'Not Found',
header: { vary: 'Accept-Encoding, Origin' } },
app: { subdomainOffset: 2, proxy: false, env: 'dev' },
originalUrl: '/bff/v1/tttare/param',
req: '<original node req>',
res: '<original node res>',
socket: '<original node socket>' }
@Body() queryParam: any body是请求体,接收请求的json字符串,将json字符串转为js对象
//log queryParam对象如下
[ { 'name ': 'tttare', sex: '男', level: 2 },
{ 'name ': 'tom', sex: '男', level: 3 },
{ 'name ': 'mick', sex: '男', level: 2 },
{ 'name ': 'nancy', sex: '女', level: 2 } ]
项目使用typedi框架实现依赖注入
//controller层引入 Inject依赖注入service
import { Inject } from 'typedi';
@Inject()
private userDataInstance: UserDataManager;//ts注入
/**********************************/
//service使用 Service做控制反转,将UserDataManager实例的产生交个框架
import { Service } from 'typedi';
@Service()
export default class UserDataManager
编写service,注入controller层调用
src\biz\example\UserDataManager.ts的ts编写逻辑处理
//给学生对象按年级 level 分类
public getTestDataWithParam(queryParamList: any[]):Map<number,any[]>{
//ts提供的Map type,和java一样,key-value存储
let map = new Map<number,any[]>();
//利用map的api进行学生分组
queryParamList.forEach(item=>{
if(map.has(item.level)){
map.get(item.level).push(item)
}else{
map.set(item.level,[item]);
}
})
return map;
}
测试编写的接口
断点调试
在vscode界面找到debugger窗口,点击驱动项目
debugger项目启动端口和dev端口一致,要先关了dev环境
debugger启动配置
debugger过程
开发常见API解析-进阶
异步相关(async await Promise)
异步操作一般是在主线程中,开辟新的线程去做耗时操作,项目中最多的便是网络请求;
-
async 修饰方法,代表此方法方法体内有异步操作,且改方法的返回值会被包装成一个Promise ,默认将方法的返回值作Promise中 resolve方法的参数
-
await 只能存在与被async修饰的方法体内,来修饰一个异步调用操作(或者说修饰一个返回Promise的方法),用来暂停主线程,等待异步调用完成,主线程才开始往下走,感觉是将原先的异步操作,变为了同步一的一种效果,一般是因为主线程下面的操作需要异步调用(网络请求)的返回值;(await 所取的参数来自于Promise中resolve函数的赋值,也就是请求成功时Promise调用的resolve方法)
-
Promise 是为异步编程提供了一种解决方案
名称:Promise,译为“承诺”,这也就表达了将来会执行的操作,代表异步操作;
状态:一共有三种状态,分别为pending(进行中)、fulfilled(已成功)和rejected(已失败)。
特点:(1)只有异步操作可以决定当前处于的状态,并且任何其他操作无法改变这个状态;
(2)一旦状态改变,就不会在变。状态改变的过程只可能是:从pending变为fulfilled和从pending变为rejected。如果状态发生上述变化后,此时状态就不会在改变了,这时就称为resolved(已定型)
Promise使用实例
public static testMethod(){
//Promise 如何实现异步操作
//将异步操作包装入Promise对象的 (resolve, reject) => {}方法中
const promise = new Promise((resolve, reject) => {
let error = '连接失败';
let value ="请求成功";
let success = false;
if (success) {
resolve(value); //pending => fulfilled
} else {
reject(error); // pending => rejected
}
});
//如何获取异步操作的执行结果
promise.then((v)=>{
//then 获取到 resolve(value) 方法的参数 value
console.log(`then:${v}`)
}).catch((e)=>{
//catch 获取 reject(error) 方法的参数 error
console.log(`catch:${e}`)
}).finally(()=>{
//finally 必须执行的处理
console.log("处理结束")
})
return promise
}
//调用testMethod方法
@post("/forTest")
async fotTest(ctx){
console.log("start...")
AccountService.testMethod();
console.log("end...")
}
执行结果
如上图所示,Promise对象已经将我们的操作变为异步操作了
async 标记方法实例
//方法和简单,async能不能让方法异步
public static async testMethod2(){
console.log("async方法执行中")
return "aaa"
}
@post("/forTest")
async fotTest(ctx){
console.log("start...")
//async 的返回值只能用Promise对象接收
let p:Promise<any> = AccountService.testMethod2();
p.then((v)=>{
//then中操作方法的返回值
console.log("取到asyn方法返回值"+v)
})
console.log("end...")
}
如上图所示,async标记的方法,方法体并不是异步的,异步操作需要Promise来实现,new Promise和Promise的then,catch,finally是异步的;故async更多是标记方法存在异步操作,并使得方法的返回值被Promise包装
IBS-BFF项目异步操作实例(async await Promise的组合运用)
//RedisUtil中的hgetall方法,讲操作redis异步进行
async hgetall(key): Promise<any> {
return new Promise((resolve, reject) => {
client.hgetall(key, (err, res) => {
//成功与失败都调用resolve,是为了方便用 await取值
if (err) {
resolve(false);
} else {
resolve(res);
}
});
});
}
//await 修饰的async方法,会直接拿到方法返回值中的 resolve参数
//await redisUtil.hgetall 可知,data为Promise resolve方法的参数,及默认取调用成功的返回值
const data = await redisUtil.hgetall(this.inSettleAccountKey);
if (data !== false) {
for (const key in data as any) {
if (data.hasOwnProperty(key)) {
if (new Date().getTime() - parseFloat(data[key]) >= this.expiredTime) {
await this.deleteAccountCalData(parseInt(key), true);
}
}
}
} else {
G.logger.error('账户缓存数据读取出错');
}
//await Promise.all参数是一个异步方法集合,dataList则为一个数据,
//长度为方法集合长度,dataList的集合对应的是每个异步方法的返回值
//all将多个异步操作放在一起成为一个新的Promise,如果都成功,则返回长度相同的返回值数组
//一旦有一个失败,则返回第一个失败操作的reject状态值,
//有点事务的意思,集合内的异步方法要么都成功,只要有一个失败,则认为他们都失败
const dataList = await Promise.all([
MeterInfoQuery.queryLineShareSchemeAccountMapData(),
this.queryLineGatewayMapData(lineIDList),
MeterInfoQuery.queryLineAccountDataMap(lineIDList),
]);
装饰器和注解
装饰器(Decorator):仅提供定义劫持,能够对类及其方法、方法入参、属性的定义并没有提供任何附加元数据的功能。
注解(Annotation):仅提供附加元数据支持,并不能实现任何操作。需要另外的 Scanner 根据元数据执行相应操作。
TS中,注解和装饰器是可以说是一个东西,缺一不可,注解提供元数据,即其可以通过修饰类和方法、参数上,为装饰器提供元数据(类信息,方法信息,参数,以及注解本身的入参),而装饰器则负责实现对元数据 进行功能的扩展;
@controller('/account')
export default class AccountController extends BaseController {
~ ~ ~ ~ ~ ~ ~ ~
}
以TS Web服务开发常用的@controller注解为例子,其定义如下
export declare const controller: (path?: string) => ClassDecorator;
注解将自身的参数 path传递给了ClassDecorator 类装饰器,而类处理器本身是一个Function
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
typedi框架的AOP和IOC
@Service()
export default class UserDataManager {
~~~~~~
}
@JsonController('/users')
export default class UsersController {
@Inject()
private userDataInstance: UserDataManager;
}
//TODO
常用工具类
import * as moment from 'moment';
const startTime = moment().format('YYYY-MM-DD HH:mm:ss');//2021-01-20 16:42:20 当前时间字符串形式
const endTime = moment()
.add(1, 'd')//'d' 时间单位: 天
.format('YYYY-MM-DD 00:00:00');//2021-01-21 00:00:00 当前时间加一天
//将指定时间戳转成时间字符串
const startTime2 = moment(1609430400000).format('YYYY-MM-DD HH:mm:ss');//2021-01-01 00:00:00
//moment 的时间单位如下
"year" | "years" | "y" |
"month" | "months" | "M" |
"week" | "weeks" | "w" |
"day" | "days" | "d" |
"hour" | "hours" | "h" |
"minute" | "minutes" | "m" |
"second" | "seconds" | "s" |
"millisecond" | "milliseconds" | "ms"
lodash工具类对象用 _ 表示,就像jquery 用 $ 一样,全部API文档地址https://www.lodashjs.com/docs
下面为项目中对 lodash 工具类的使用实例
let params = [{
name : 'aaa',
age : 12
},{
name : 'bbb',
age : 14
} ,{
name : 'CCC',
age : 11
},{
name : 'ddd',
age : 9
}]
//G._.cloneDeep 深克隆
let newParams = G._.cloneDeep(params);
//G._.get 数组取值,第一个参数为数组,第二个为path集合
//path 第一个值为索引,第二个为属性,类似与面包屑取值,层层向下取值
let getParams = G._.get(params,[1,"name"]);// bbb
//G._.remove 便利数组,将满足条件的元素从数组中移除
G._.remove(params,param =>{
return param.age > 13
})
//将多维数组 递归为一维数组
let newArray = G._.flattenDeep([1, [2, [3, [4]], 5]]);//[1,2,3,4,5]
//将数组按 size=2分组,无法被分割成全部等长的区块,那么最后剩余的元素将组成一个区块
let chunkArray = G._.chunk(newArray,2);//[[1,2],[3,4],[5]]
//G._.uniq 数组元素去重
let uniqArr = G._.uniq([1,2,3,3,3,5,5]);//[1,2,3,5]
//对长度小于2的字符串,进行前或后的拼接 0 补位,如果长度大于等于2则不处理
let start = G._.padStart("9", 2, '0');//09
let end = G._.padEnd("9", 2, '0');//90
let end2 = G._.padEnd("19", 2, '0');//19
let initList = [[{text:"卧推"}],[{text:"深蹲"}],[{text:"侧平举"}]];
//将多维数组变成一维数组
let flatMap = G._.flatMapDeep(initList);//[{text:"卧推"},{text:"深蹲"},{text:"侧平举"}]
//连个数组元素取交集
let interArr = G._.intersection([1,2,4,5],[2,3,4]);//[2,4]
//集合对象按属性排序 按age 升序排列
let orderArr = G._.orderBy(newParams, ['age'], ['asc'])
//按字段分组
const groupData = G._.groupBy(data.data, item => {
return item.operator;
});
return "aaa"