【知识总结】开发提效50%的Javascript常用函数

这篇博客总结了JavaScript中处理数组和对象的各种实用技巧,包括数组去重、深拷贝、合并、对象数组去重、取交集、处理嵌套数组、创建指定长度数组、数据类型判断等。还介绍了防抖、节流、滚动操作以及遍历树节点等函数式编程方法。
摘要由CSDN通过智能技术生成

1、简单的数组去重

扩展运算符 + es6

const arr = ['a','b','c','a','b'];
const newArr = [...new Set(arr)]

Array.from + Set

const arr = ['a','b','c','a','b'];
const newArr = Array.from(new Set(arr))

2、数组深拷贝

只拷贝一层,适合简单的拷贝,不适用于数组对象:

const arr = [1,2,3]

const arr1 = arr.slice(0)
const arr2 = arr.concat()
const arr3 = [...arr]
const arr4 = Array.from(arr)

使用JSON:

const arr = [{age: 1}, {age: 2}, {age: 3}];
const newArr = JSON.parse(JSON.stringify(arr))

⚠️注意:这种方式不可靠,需要注意以下几种情况

const obj = {
  nan: NaN, // NaN拷贝后变成null
  infinityMax:1.7976931348623157E+10308,  // 浮点数的最大值拷贝后变成null
  infinityMin:-1.7976931348623157E+10308, // 浮点数的最小值拷贝后变成null
  undef: undefined, // 拷贝后直接丢失
  fun: () => 'func', // 拷贝后直接丢失
  date:new Date, // 时间类型拷贝后会被变成字符串类型数据
}

JSON.stringify 的目的是将数据转成 json 格式,而 json 有自己的规范,并不是 js 专属,它是跨语言的,各种语言都用统一的 json 格式来通信,所以只能支持各种语言常用的数据类型。

更可靠的实现方式:

function deepClone(target){
	if(Array.isArray(target)){	//处理数组
		return target.map(item => deepClone(item))
	}
	if(Object.prototype.toString.call(target)==='[object Object]'){
	//先将对象转化为二维数组,再将二维数组转化为对象
	//所以map方法将二维数组里的元素进行深拷贝完了再转化为对象
		return Object.fromEntries(Object.entries(target).map(([k, v]) => [k, deepCopy(v)]))
	}
	return target
}

3、数组合并

扩展运算符,不改变原数组:

const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [...arr1, ...arr2]

扩展运算符,改变原数组:

const arr1 = [1, 2];
const arr2 = [3, 4];
arr1.push(...arr2)

concat方法:

const arr1 = [1, 2];
const arr2 = [3, 4];
const newArr = arr1.concat(arr2)

apply方法:

const arr1 = [1, 2];
const arr2 = [3, 4];
arr1.push.apply(arr1, arr2)

4、对象数组去重

arrDistinctByKey(arr, key){
	const temp = new Map()	//使用Map可以对比多类型,不限于字符串
	return arr.filter((item) => !temp.has(item[key]) && temp.set(item[key], true))
}
//常用于过滤列表
const list = [{id: 1, name: 'xiaoming'}, {id: 1, name: 'xiaoming'}, {id: 2, name: 'xiaoliang'}];
const newArr = arrDistinctByKey(list, 'id');
// newArr: [{id: 1, name: 'xiaoming'}, {id: 2, name: 'xiaoliang'}]

5、对象数组取交集

arrIntersectionByKey(arr1,arr2,key){
	const arr2Keys = arr2.map(item => item[key])
	return arr1.filter(item => arr2Keys.includes(item[key]))
}

const receivedCoupons = [{ name: '立减券' },{ name: '折扣券' }]; 
const welfareCoupons = [{ stockId: '立减券' }];

arrIntersectionByKey(receivedCoupons,welfareCoupons, 'name');
// [{ name: '立减券' }]

6、使用flat和flatMap方法处理嵌套数组

数据样例:

// 选项组数组,这是一个筛选组件的数据
const optionsGroup = [
  {
    groupTitle: '资源类型',
    groupId: 'current',
    mode: 'multi',
    options: [
      { text: '直流桩', value: [1, 3], active: true },
      { text: '交流桩', value: [2, 4, 5], active: false },
    ],
  },
  {
    groupTitle: '通勤方式',
    groupId: 'commute',
    mode: 'multi',  // 多选
    options: [
      { text: '公交', value: 0, active: true },
      { text: '地铁', value: 1, active: true },
      { text: '驾车', value: 1, active: false },
    ],
  },
];

尝试将上面数组处理为下面这样的数据结构:

[
  { text: '公交', value: 0, active: true, groupId: 'commute' },
  ...
],

使用flatMap:相当于在map的功能基础上,加上了flat方法的效果,flat方法的效果是将数组降维一层

const activated = optionGroup
	.flatMap(item => item.options.map(option => ({...option, groupId: item.groupId}) ))
	.filter(item => item.active)

7、快速创建一个指定长度的数组并填充内容

const array = new Array(100).fill('');
// (100) ['', '', ..., '']
const array = Array.from(new Array(100).keys())
const array = Array.from({length: 100}, (v,i) => i);

8、利用数组交换值

[a,b] = [b,a]

9、替代短路或,使用includes,该方式也是可避免代码复杂度过高的有效方法之一

if(from == 'a' || from == 'b' || from == 'c'){}

if([a,b,c].includes(from)){}

10、使用Map代替switch或多个if判断,该方式也是可避免代码复杂度过高的有效方法之一

switch写法:

function getStatusText(status) {
  switch (status) {
    case 1:
      return '待发货';
    case 2:
      return '已发货';
    case 3:
      return '已完成';
    default:
      return '';
  }
}

Map写法:

const statusMap = new Map([
  [1, '待发货'],
  [2, '已发货'],
  [3, '已完成'],
]); // 这种写法的内部执行的算法实际上也是循环执行set
const statusText = statusMap.get(statusMap)

注意区分 Object 和 Map,只有模拟现实世界的实体对象时,才使用 Object。如果只是需要key: value的数据结构,使用 Map 结构。因为 Map 有内建的遍历机制。

11、更可靠的判断数据类型

typeof 检测一些基本的数据类型,正则、{}、[]、null输出结果为object:

 console.log(typeof /\d/); //object
 console.log(typeof {}); //object
 console.log(typeof []); //object
 console.log(typeof (null)); //object
 console.log(typeof 123); //number
 console.log(typeof true); //boolean
 console.log(typeof function () {}); //function
 console.log(typeof (undefined)); //undefined

A instanceof B 判断a的构造器是否为b:

function b = {}
let a = new b
console.log(a instanceof b); //true
console.log(b instanceof Object); //true
let arr = [1,2,3,4];
console.log(arr instanceof Array); //true

以上两种方法有有其局限性,推荐使用更可靠的判断数据类型方法

function judgeDataType(val, type) {
  const dataType = Object.prototype.toString.call(val).replace(/\[object (\w+)\]/, "$1").toLowerCase();
  return type ? dataType === type : dataType;
}
console.log(judgeDataType("young")); // "string"
console.log(judgeDataType(20190214)); // "number"
console.log(judgeDataType(true)); // "boolean"
console.log(judgeDataType([], "array")); // true
console.log(judgeDataType({}, "array")); // false

12、检查是否为空对象

Object.keys({}).length //0
JSON.stringify({})==='{}'

13、校验数据类型

export const typeOf = function(obj){
	return Object.prototype.toString.call(obj).slice(8,-1).toLowerCase()
}

14、防抖

export const debounce = (()=>{
	let timer = null
	return (callback, wait = 800)=>{
		timer && cleartTimeout(timer)
		timer = setTimeout(callback, wait)
	}
})()

使用:

const loadList =()=>{
    debounce(() => {
      console.log('加载数据')
    }, 500)
  }

15、节流

export const throttle =(()=>{	
	let last = 0
	return (callback, wait = 800) =>{
		let now = + new Date()
		if(now - last > wait){
			callback()
			last = now
		}
	}
})()

16、滚动到页面顶部

export const scrollToTop =()=>{
	const height = document.documentElement.scrllTop || document.body.scrollTop
	if(height>0){
		window.requestAnimationFrame(scrollTop)
		window.scrollTo(0, height - height/8)
	}
}

17、滚动到元素位置

export const smoonthScroll = element => {
	document.querySelector(element).scrollIntoView({
		behavior: 'smooth'
	})
}

使用:

smoothScroll('#target'); // 平滑滚动到 ID 为 target 的元素

18、遍历树节点

export const foreachTree = (data, callback, childrenName = 'children'){
	for(let i=0; i<data.length; i++){
		callback(data[i])
		if(data[i][childrenName] && data[i][childrenName].length > 0){
			foreachTree(data[i][childrenName], callback, childrenName)
		}
	}
}

假设我们要从树状结构数据中查找 id 为 9 的节点:

const treeData = [{
  id: 1,
  label: '一级 1',
  children: [{
    id: 4,
    label: '二级 1-1',
    children: [{
      id: 9,
      label: '三级 1-1-1'
    }, {
      id: 10,
      label: '三级 1-1-2'
    }]
  }]
 }, {
  id: 2,
  label: '一级 2',
  children: [{
    id: 5,
    label: '二级 2-1'
  }, {
    id: 6,
    label: '二级 2-2'
  }]
  }, {
    id: 3,
    label: '一级 3',
    children: [{
      id: 7,
      label: '二级 3-1'
    }, {
      id: 8,
      label: '二级 3-2'
    }]
}],

let result
foreachTree(data, (item) => {
  if (item.id === 9) {
    result = item
  }
})
console.log('result', result)  // {id: 9,label: "三级 1-1-1"}   

19、获取数组最后一项

const arr = [1, 2, 3, 4, 5];
arr[arr.length - 1]  // 5
arr.slice(-1) // [5]

slice 也并不会修改原来的数组

20、循环执行异步操作后执行

在执行异步任务的时候,都会返回一个可以停止事件的函数,调用函数就可以停止任务。任务的多少是取决于实际操作的,而不是你决定的。你需要将这些任务都关闭后,再去做其他操作。

const queue = [fn1, fn2, ...]
async function start(){
	await Promise.all(
		queue.map(async(callback)=>{
			await callback()
		})
	)
	do something()
}  

21、异步队列执行

有时候频繁接收一些任务,但是还没来得及执行,所以需要把这些任务都放到队列里面,然后再按顺序执行。这时候就可以写一个数组来存储这些任务。

const queue = [fn1, fn2, ...]
async function start(){
	if(queue.length == 0) return
	await queue[0]()
	queue.shift()
	start()
}
for(let i=0; i<queue.length; i++){
	await queue[0]()
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

椰卤工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值