JS常见方法(一)
本文为记录JS中常见的方法,您可以将其封装为你自己的工具函数,方便在任何项目中使用。如下方法为我工作期间遇到的实际需求整理得来,其中有些借鉴了他人的代码精华(距离时间有些久远,有些已经找不到原文了),如果您有更好的方法,欢迎留言讨论!篇幅有限,本文将会持续更新。
一、数组去重
1.Set()
适用场景:数组内的元素全部为基本数据类型(引用类型会失效)
let arr = [1, '2', 3, '2', false, false, true]
let newArr = [...new Set(arr)]
console.log(newArr)

2.利用对象
适用场景:数组内的元素全部为对象,且有唯一值
数据源:
let arr = [{id: 1,name: 'zhangsan'
}, {id: 2,name: 'lisi'
}, {id: 1,name: 'zhangsan'
}]
JS:
/** * 数组去重* @param Array {arr} 原始数组* @param String {id} 数组元素的唯一标识,默认为id*/
function arrDeduplication(arr, id = 'id') {if (!(arr instanceof Array)) return arrlet obj = {}let newArr = []arr.forEach(k => {if (!obj[k[id]]) {obj[k[id]] = true //这里仅为标记此属性已经被添加了newArr.push(k)}})return newArr
}
结果:

数组去重的方法有很多,这里仅列举常用且简单的
二、数组按固定长度分割
使用场景:例如:可滑动菜单,每页8个菜单,每个菜单根据权限动态渲染
数据源:
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
JS:
/*** 数组按固定长度分割* @param Array {arr} 原始数组* @param Number {len} 长度*/
function cutArray(arr, len) {if (!(arr instanceof Array) || !len) return arrlet index = 0;let newArr = [];while (index < arr.length) {newArr.push(arr.slice(index, index += len));}return newArr;
}console.log(cutArray(arr, 5))
结果:

三、数组排列组合
适用场景:商品sku
数据源:
let arr = [['中杯', '大杯', '超大杯'], //份量['加珍珠', '加椰果', '加西米露'] //小料
]

JS:
/** * 数组排列组合* @param Array {arr} 原始数组(二维数组)*/
function doCombination(arr) {var count = arr.length - 1; //数组长度(从0开始)var tmp = [];var totalArr = []; // 总数组return doCombinationCallback(arr, 0); //从第一个开始//js 没有静态数据,为了避免和外部数据混淆,需要使用闭包的形式function doCombinationCallback(arr, curr_index) {for (let val of arr[curr_index]) {tmp[curr_index] = val; //以curr_index为索引,加入数组//当前循环下标小于数组总长度,则需要继续调用方法if (curr_index < count) {doCombinationCallback(arr, curr_index + 1); //继续调用} else {totalArr.push(tmp); //(直接给push进去,push进去的不是值,而是值的地址)}//js 对象都是 地址引用(引用关系),每次都需要重新初始化,否则 totalArr的数据都会是最后一次的 tmp 数据;let oldTmp = tmp;tmp = [];for (let index of oldTmp) {tmp.push(index);}}return totalArr;}
}
console.log(doCombination(arr))
结果:

四、数组排序
1.sort
字符串数据源:
let arr = ['张三', '李四', '安徒生', 'IKUN', 'FBI','Jay','vitas', 'jack', 'mary', '123', '3', '24']
1.1 数字>汉字>字母 (数字按1-9,中文按拼音,英文按a-Z)
console.log(arr.sort((a, b) => a.localeCompare(b)))

数字数据源:
let arr = [1, 3, 4, 5, 16, 6, 32]
1.2 数字从小到大排序
console.log(arr.sort((a, b) => a - b))

1.3 数字从大到小排序
console.log(arr.sort((a, b) => b - a))

1.4 按照给定规则数组排序
数据源:
let arr = [{field: 'name',value: 2
}, {field: 'age',value: 3
}, {field: 'id',value: 1
}, {field: 'sex',value: 4
}]
排序规则数组:
// 排序规则数组
let order = ['id', 'name', 'job', 'age', 'city', 'sex']
JS:
console.log(arr.sort((a, b) => {return order.indexOf(a.field) - order.indexOf(b.field)
}))
结果:

排序方法还有很多,例如:快速排序、冒泡排序、插入排序、归并排序等
五、扁平化数组转树形结构(不用递归)
适用场景:将树结构转化为一维数组
来源:https://juejin.cn/post/6983904373508145189
数据源:
let arr = [{id: 1,name: '部门1',pid: 0},{id: 2,name: '部门2',pid: 1},{id: 3,name: '部门3',pid: 1},{id: 4,name: '部门4',pid: 3},{id: 5,name: '部门5',pid: 4},
]
思路:
- 树结构首先需要考虑顶级目录,单个顶级目录的本质是一个对象,那么我们首先可以只考虑顶级目录,将所有的顶级目录找出来
- 有了顶级目录后,我们就需要考虑子目录了,我们可以在顶级目录上使用children来存放
- 区别顶级目录和子目录的方法是pid(父级id)和id,如果pid为0则是顶级目录,否则是子目录
- 子目录需要使用父级目录的id,也就是pid
- 由于是扁平化的数组,不考虑递归的话,我们可以借用对象引用来实现。将数组元素的id作为对象的键,数组元素做为值。然后在遍历对象的过程中,我们需要想办法让每一个值放到对应的位置
- 我们可以利用对象的浅拷贝来实现多层目录的修改
方法一:双循环
function arrayToTree(items) {const result = []; // 存放结果集const itemMap = {}; // // 先将数组转成对象存储for (const item of items) {itemMap[item.id] = {...item}}for (const item of items) {const id = item.id;const pid = item.pid;const treeItem = itemMap[id];//pid === 0即当前为顶级目录if (pid === 0) {result.push(treeItem);} else {if (itemMap[pid]) {itemMap[pid].children = itemMap[pid].children ? itemMap[pid].children : []itemMap[pid].children.push(treeItem)}}}return result;
}
方法二:单循环
function arrayToTree2(items) {const result = []; // 存放结果集const itemMap = {}; // for (const item of items) {const id = item.id;const pid = item.pid;if (!itemMap[id]) {itemMap[id] = {children: [],}}itemMap[id] = {...item,children: itemMap[id]['children']}const treeItem = itemMap[id];//pid === 0即当前为顶级目录if (pid === 0) {result.push(treeItem);} else {if (!itemMap[pid]) {itemMap[pid] = {children: [],}}itemMap[pid].children.push(treeItem)}}return result;
}
结果:

代码分析:
1.首先,两种方法都是利用了对象引用来实现的,通过将元素的id作为键,元素本身作为值。
2.代码的关键都在“if(pid === 0)”之后,乍一看只是修改了itemMap的值,好像对最终返回的result没有影响,但我们仔细来看。函数中,在对itemMap的id属性进行赋值之后,其他的操作本质上只是改变了对象的引用,itemMap中的5个部门对象在内存中一直没有变,所以我们直接对这5个部门对象进行的修改,那么只要是引用它们的数据都会受影响。
3.所以我们可以直接修改itemMap上的属性,无需考虑嵌套关系。
六、日期
注意:在IOS16以下版本中,new Date()的参数中不支持传入“2022-03-27”这种格式,需要换为“2022/03/27”。以下方法未使用多参数类型,若使用TS,可以利用函数重载。
dateStr = dateStr.replace(/\-/g, '/')
1.日期格式化
//年月日 时分秒
function formatDate(originVal) {const dt = new Date(originVal)const y = dt.getFullYear()const m = (dt.getMonth() + 1 + '').padStart(2, '0')const d = (dt.getDate() + '').padStart(2, '0')const hh = (dt.getHours() + '').padStart(2, '0')const mm = (dt.getMinutes() + '').padStart(2, '0')const ss = (dt.getSeconds() + '').padStart(2, '0')return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
}//年月日
function formatDate2(originVal) {const dt = new Date(originVal)const y = dt.getFullYear()const m = (dt.getMonth() + 1 + '').padStart(2, '0')const d = (dt.getDate() + '').padStart(2, '0')return `${y}-${m}-${d}`
}
2.前一天
//dateStr为当前日期
formatTime2(new Date(dateStr).getTime() - 24 * 60 * 60 * 1000)
3.后一天
//dateStr为当前日期
formatTime2(new Date(dateStr).getTime() + 24 * 60 * 60 * 1000)
七、节流防抖
来源:uview2
节流
节流的意思是,规定时间内,只触发一次。比如我们设定500ms,在这个时间内,无论点击按钮多少次,它都只会触发一次。具体场景可以是抢购时候,由于有无数人 快速点击按钮,如果每次点击都发送请求,就会给服务器造成巨大的压力,但是我们进行节流后,就会大大减少请求的次数。防抖
防抖的意思是,在连续的操作中,无论进行了多长时间,只有某一次的操作后在指定的时间内没有再操作,这一次才被判定有效。具体场景可以搜索框输入关键字过程中实时 请求服务器匹配搜索结果,如果不进行处理,那么就是输入框内容一直变化,导致一直发送请求。如果进行防抖处理,结果就是当我们输入内容完成后,一定时间(比如500ms)没有再 输入内容,这时再触发请求。
let timer; letflag
/*** 节流原理:在一定时间内,只能触发一次** @param {Function} func 要执行的回调函数* @param {Number} wait 延时的时间* @param {Boolean} immediate 是否立即执行* @return null*/
function throttle(func, wait = 500, immediate = true) {if (immediate) {if (!flag) {flag = true// 如果是立即执行,则在wait毫秒内开始时执行typeof func === 'function' && func()timer = setTimeout(() => {flag = false}, wait)}} else if (!flag) {flag = true// 如果是非立即执行,则在wait毫秒内的结束处执行timer = setTimeout(() => {flag = falsetypeof func === 'function' && func()}, wait)}
}
let timeout = null/*** 防抖原理:一定时间内,只有最后一次操作,再过wait毫秒后才执行函数** @param {Function} func 要执行的回调函数* @param {Number} wait 延时的时间* @param {Boolean} immediate 是否立即执行* @return null*/
function debounce(func, wait = 500, immediate = false) {// 清除定时器if (timeout !== null) clearTimeout(timeout)// 立即执行,此类情况一般用不到if (immediate) {const callNow = !timeouttimeout = setTimeout(() => {timeout = null}, wait)if (callNow) typeof func === 'function' && func()} else {// 设置定时器,当最后一次操作后,timeout不会再被清除,所以在延时wait毫秒后执行func回调方法timeout = setTimeout(() => {typeof func === 'function' && func()}, wait)}
}
八、深克隆
数据源:
let obj = {a: 123,b: 'zhangsan',c: () => {console.log('ccc')},d: [1, '2', {name: 'linxi',age: 18,info: {city: 'zz'}}],e: null,f: '',g: false,h: Symbol('h'),i: undefined,j: NaN
}
1. JSON.parse(JSON.stringify())
适用场景:对象属性中没有函数、Symbol、undefined、NaN这4中类型
JSON.parse(JSON.stringify(obj))

2.完整版
function deepCopy(value) {if (value instanceof Function) return valueelse if (value instanceof Array) {var newValue = []for (let i = 0; i < value.length; ++i) newValue[i] = deepCopy(value[i])return newValue} else if (value instanceof Object) {var newValue = {}for (let i in value) newValue[i] = deepCopy(value[i])return newValue} else return value
}

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
