项目常用的 JavaScript 代码片段
# 项目常用的 JavaScript 代码片段
# 1. 下载一个 excel 文档
同时适用于 word,ppt 等浏览器不会默认执行预览的文档,也可以用于下载后端接口返回的流数据。
//下载一个链接
const download = (link, name) => {
//如果没有提供名字,从给的Link中截取最后一坨
if (!name) name = link.slice(link.lastIndexOf('/') + 1)
let eleLink = document.createElement('a')
eleLink.download = name
eleLink.style.display = 'none'
eleLink.href = link
document.body.appendChild(eleLink)
eleLink.click()
document.body.removeChild(eleLink)
}
2
3
4
5
6
7
8
9
10
11
12
使用方式
download('http://111.229.14.189/file/1.xlsx')
download('http://111.229.14.189/gk-api/util/download?file=1.jpg')
download('http://111.229.14.189/gk-api/util/download?file=1.mp4')
2
3
# 2. 在浏览器中自定义下载一些内容
场景:下载一些 DOM 内容,下载一个 JSON 文件
/**
* 浏览器下载静态文件
* @param {String} name 文件名
* @param {String} content 文件内容
*/
const downloadFile = (name, content) => {
if (typeof name == 'undefined') throw new Error('The first parameter name is a must')
if (typeof content == 'undefined') throw new Error('The second parameter content is a must')
if (!(content instanceof Blob)) content = new Blob([content])
const link = URL.createObjectURL(content)
// 调用方法1
download(link, name)
}
2
3
4
5
6
7
8
9
10
11
12
13
使用方式
downloadFile('1.txt', 'lalalallalalla')
downloadFile('1.json', JSON.stringify({ name: 'hahahha' }))
2
# 3. 提供一个图片链接,点击下载
图片、pdf 等文件,浏览器会默认执行预览,不能调用 download 方法进行下载,需要先把图片、pdf 等文件转成 blob,再调用 download 方法进行下载,转换的方式是使用 axios 请求对应的链接。
//可以用来下载浏览器会默认预览的文件类型,例如mp4,jpg等
import axios from 'axios'
//提供一个link,完成文件下载
const downloadByLink = (link, fileName) => {
axios
.request({
url: link,
responseType: 'blob', //关键代码,让axios把响应改成blob
})
.then((res) => {
const link = URL.createObjectURL(res.data)
// 调用方法1
download(link, fileName)
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
使用方式
downloadByLink('http://xxx.com/xxx.ppt')
# 4. 防抖
在一定时间间隔内,多次调用一个方法,只会执行一次。 这个方法的实现是从 Lodash 库中 copy 的。
/**
*
* @param {*} func 要进行debouce的函数
* @param {*} wait 等待时间,默认500ms
* @param {*} immediate 是否立即执行
*/
const debounce = (func, wait = 500, immediate = false) => {
let timeout
return function () {
let context = this
let args = arguments
if (timeout) clearTimeout(timeout)
if (immediate) {
// 如果已经执行过,不再执行
let callNow = !timeout
timeout = setTimeout(function () {
timeout = null
}, wait)
if (callNow) func.apply(context, args)
} else {
timeout = setTimeout(function () {
func.apply(context, args)
}, wait)
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
使用方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input id="input" />
<script>
function onInput() {
console.log('1111')
}
const debounceOnInput = debounce(onInput)
// 在Input中输入,多次调用只会在调用结束之后,等待500ms触发一次
document.getElementById('input').addEventListener('input', debounceOnInput)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
如果第三个参数immediate传 true,则会立即执行一次调用,后续的调用不会在执行,可以自己在代码中试一下
# 5. 节流
多次调用方法,按照一定的时间间隔执行。 这个方法的实现也是从 Lodash 库中 copy 的。
/**
* 节流,多次触发,间隔时间段执行
* @param {Function} func
* @param {Int} wait
* @param {Object} options
*/
const throttle = (func, wait = 500, options) => {
let timeout, context, args
let previous = 0
if (!options) options = { leading: false, trailing: true }
let later = function () {
previous = options.leading === false ? 0 : new Date().getTime()
timeout = null
func.apply(context, args)
if (!timeout) context = args = null
}
const throttled = function () {
let now = new Date().getTime()
if (!previous && options.leading === false) previous = now
let remaining = wait - (now - previous)
context = this
args = arguments
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
previous = now
func.apply(context, args)
if (!timeout) context = args = null
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining)
}
}
return throttled
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
第三个参数还有点复杂,options
- leading,函数在每个等待时延的开始被调用,默认值为false
- trailing,函数在每个等待时延的结束被调用,默认值是true
- leading-false,trailing-true:默认情况,即在延时结束后才会调用函数
- leading-true,trailing-true:在延时开始时就调用,延时结束后也会调用
- leading-true, trailing-false:只在延时开始时调用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<input id="input" />
<script>
function onInput() {
console.log('1111')
}
const throttleOnInput = throttle(onInput)
// 在Input中输入,每隔500ms执行一次代码
document.getElementById('input').addEventListener('input', throttleOnInput)
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 6. cleanObject
去除对象中 value 为空(null,undefined,'')的属性 使用场景是:接口传参某个字段为空剔除不传。
const cleanObject = (object) => {
if (!object) return {}
let result = {}
Object.keys(object).forEach((key) => {
const value = object[key]
if (value) result = { ...result, [key]: value }
})
return result
}
2
3
4
5
6
7
8
9
使用方式
const newObj = cleanObject({
name: '',
pageSize: 10,
page: 1,
})
console.log('res', res) // 输出{ page: 1, pageSize: 10 }
2
3
4
5
6
# 7. 获取文件后缀名
使用场景:上传文件判断后缀名
/**
* 获取文件后缀名
* @param {String} fileName
*/
const getExt = (fileName) => {
if (typeof fileName == 'string') return fileName.split('.').pop().toLowerCase()
throw new Error('fileName must be a string type')
}
2
3
4
5
6
7
8
使用方式
getExt('1.mp4') // -> mp4
# 8. 复制内容到剪贴板
/**
*原理:
*创建一个textare元素并调用select()方法选中
*document.execCommand('copy')方法,拷贝当前选中内容到剪贴板。
*/
const copyToBoard = (value) => {
const element = document.createElement('textarea')
document.body.appendChild(element)
element.value = value
element.select()
if (document.execCommand('copy')) {
document.execCommand('copy')
document.body.removeChild(element)
return true
}
document.body.removeChild(element)
return false
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
使用方式
// 如果复制成功返回true
copyToBoard('lalallala')
2
# 9. 休眠多少毫秒
/**
* 休眠xxxms
* @param {Number} milliseconds
*/
const sleep = (ms) => {
return new Promise((resolve) => setTimeout(resolve, ms))
}
2
3
4
5
6
7
使用方式
const fetchData = async () => {
await sleep(1000)
}
2
3
# 10. 生成随机字符串
使用场景:用于前端生成随机的 ID,毕竟现在的 Vue 和 React 都需要绑定 key
/**
* 生成随机id
* @param {*} length
* @param {*} chars
*/
const uuid = (length = 8, chars) => {
chars = chars || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let result = ''
for (let i = length; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)]
return result
}
2
3
4
5
6
7
8
9
10
11
使用方式
// 第一个参数指定位数,第二个字符串指定字符,都是可选参数,如果都不传,默认生成8位
uuid()
2
# 11. 简单的深拷贝
缺陷:只拷贝对象、数组以及对象数组,对于大部分场景已经足够
/**
*深拷贝
* @param {*} obj
* @returns
*/
const deepCopy = (obj) => {
if (typeof obj != 'object') return obj
if (obj == null) return obj
return JSON.parse(JSON.stringify(obj))
}
2
3
4
5
6
7
8
9
10
使用方式
const person = { name: 'xiaoming', child: { name: 'Jack' } }
deepCopy(person)
2
# 12. 数组去重
原理:利用 Set 中不能出现重复元素的特性
/**
* 数组去重
* @param {*} arr
*/
const uniqueArray = (arr) => {
if (!Array.isArray(arr)) throw new Error('The first parameter must be an array')
if (arr.length == 1) return arr
return [...new Set(arr)]
}
2
3
4
5
6
7
8
9
使用方式
uniqueArray([1, 1, 1, 1, 1]) // [1]
# 13. 对象转化为 FormData 对象
使用场景:上传文件时我们要新建一个 FormData 对象,然后有多少个参数就 append 多少次,使用该函数可以简化逻辑
/**
* 对象转化为formdata
* @param {Object} object
*/
const getFormData = (object) => {
const formData = new FormData()
Object.keys(object).forEach((key) => {
const value = object[key]
if (Array.isArray(value)) {
value.forEach((subValue, i) => formData.append(key + `[${i}]`, subValue))
} else {
formData.append(key, object[key])
}
})
return formData
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
使用方式
const req = {
file: 'xxx',
userId: 1,
phone: '15198763636',
}
getFormData(req)
2
3
4
5
6
# 14. 保留到小数点以后 n 位
使用场景:JS 的浮点数超长,有时候页面显示时需要保留 2 位小数
const cutNumber = (number, no = 2) => {
if (typeof number != 'number') number = Number(number)
return Number(number.toFixed(no))
}
2
3
4
使用方式
cutNumber(3.1415926)
# 15. 获取基础 URL
const getBaseURL = (url) => url.replace(/[?#].*$/, '')
使用方式
getBaseURL('http://url.com/page?name=Adam&surname=Smith') // 'http://url.com/page'
# 16. 判断网址是否为绝对网址
const isAbsoluteURL = (str) => /^[a-z][a-z0-9+.-]*:/.test(str)
使用方式
isAbsoluteURL('https://google.com') // true
isAbsoluteURL('ftp://www.myserver.net') // true
isAbsoluteURL('/foo/bar') // false
2
3
# 17. 获取 URL 参数作为对象
const getURLParameters = (url) =>
(url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(
(a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a),
{},
)
2
3
4
5
使用方式
getURLParameters('google.com') // {}
getURLParameters('http://url.com/page?name=Adam&surname=Smith') // {name: 'Adam', surname: 'Smith'}
2
# 18. 检查元素是否包含另一个元素
const elementContains = (parent, child) => parent !== child && parent.contains(child)
使用方式
elementContains(document.querySelector('head'), document.querySelector('title')) // true
elementContains(document.querySelector('body'), document.querySelector('body')) // false
2
# 19. 获取元素的所有祖先
const getAncestors = (el) => {
let ancestors = []
while (el) {
ancestors.unshift(el)
el = el.parentNode
}
return ancestors
}
2
3
4
5
6
7
8
使用方式
getAncestors(document.querySelector('nav')) // [document, html, body, header, nav]
# 20. 平滑滚动元素进入视图
const smoothScroll = (element) =>
document.querySelector(element).scrollIntoView({
behavior: 'smooth',
})
2
3
4
使用方式
smoothScroll('#fooBar')
smoothScroll('.fooBar')
2
# 21. 处理元素外的点击
const onClickOutside = (element, callback) => {
document.addEventListener('click', (e) => {
if (!element.contains(e.target)) callback()
})
}
2
3
4
5
使用方式
onClickOutside('#my-element', () => console.log('Hello'))
# 22. 生成 UUID
const UUIDGeneratorBrowser = () =>
([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
(c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16),
)
2
3
4
使用方式
UUIDGeneratorBrowser() // '0568875f-6106-4621-9bab-6be33b410027'
# 23. 获取选中的文本
const getSelectedText = () => window.getSelection().toString()
使用方式
getSelectedText()
# 24. 给 HTML 元素添加样式
const addStyles = (el, styles) => Object.assign(el.style, styles)
使用方式
addStyles(document.getElementById('my-element'), {
background: 'red',
color: '#ffff00',
fontSize: '3rem',
})
2
3
4
5
# 25. 切换全屏模式
const fullscreen = (mode = true, el = 'body') =>
mode ? document.querySelector(el).requestFullscreen() : document.exitFullscreen()
2
使用方式
fullscreen()
fullscreen(false)
2
# 26. 检测 Caps Lock 是否开启
<form>
<label for="username">Username:</label>
<input id="username" name="username" />
<label for="password">Password:</label>
<input id="password" name="password" type="password" />
<span id="password-message" style="display: none">大写锁定已开启</span>
</form>
<script>
const el = document.getElementById('password')
const msg = document.getElementById('password-message')
el.addEventListener('keyup', (e) => {
msg.style = e.getModifierState('CapsLock') ? 'display: block' : 'display: none'
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
# 27. 看日期是否有效
const isDateValid = (...val) => !Number.isNaN(new Date(...val).valueOf())
使用方式
isDateValid('December 17, 1995 03:24:00') // true
isDateValid('1995-12-17T03:24:00') // true
isDateValid('1995-12-17 T03:24:00') // false
isDateValid('Duck') // false
isDateValid(1995, 11, 17) // true
isDateValid(1995, 11, 17, 'Duck') // false
isDateValid({}) // false
2
3
4
5
6
7
# 28. 从 Date 中获取冒号时间
const getColonTimeFromDate = (date) => date.toTimeString().slice(0, 8)
使用方式
getColonTimeFromDate(new Date()) // '09:39:08'
# 29. 从 Date 生成 UNIX 时间戳
const getTimestamp = (date = new Date()) => Math.floor(date.getTime() / 1000)
使用方式
getTimestamp() // 1720057242
# 30. 查看当前用户的首选语言
const detectLanguage = (defaultLang = 'en-US') =>
navigator.language ||
(Array.isArray(navigator.languages) && navigator.languages[0]) ||
defaultLang
2
3
4
使用方式
detectLanguage() // 'zh-CN'
# 31. 查看用户偏好的配色方案
const prefersDarkColorScheme = () =>
window && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
2
使用方式
prefersDarkColorScheme() // false
# 32. 查看设备是否支持触摸事件
const supportsTouchEvents = () => window && 'ontouchstart' in window
使用方式
supportsTouchEvents() // true
# 33. 通过 key 对数组对象进行分组
目的:是将数组中的对象按指定的 key 进行分组。
/* acc 参数是累加分组对象的累加器。obj 参数表示数组中的每个对象。
在 reduce() 方法内部,使用扩展运算符 (...acc) 返回一个新对象。这将创建 accumulator 对象的浅表副本,以便可以在不更改原始对象的情况下对其进行修改。
新对象的属性设置为与 key 参数的值相匹配的键。该属性的值是一个包含被迭代对象的数组。
(acc[obj[key]] || []) 表达式检查该属性是否存在于累加器对象中。如果不存在,则返回一个空数组。展开运算符用于将数组与正在迭代的当前对象连接起来。
最后,groupBy() 函数返回包含分组对象的累加器对象。
*/
const groupBy = (arr, key) =>
arr.reduce((acc, obj) => ({ ...acc, [obj[key]]: [...(acc[obj[key]] || []), obj] }), {})
2
3
4
5
6
7
8
使用方式
const people = [
{ name: 'Alice', age: 21 },
{ name: 'Bob', age: 22 },
{ name: 'Charlie', age: 21 },
{ name: 'David', age: 23 },
{ name: 'Eve', age: 22 },
]
console.log(groupBy(people, 'age'))
/* {
21: [
{ name: "Alice", age: 21 },
{ name: "Charlie", age: 21 }
],
22: [
{ name: "Bob", age: 22 },
{ name: "Eve", age: 22 }
],
23: [{ name: "David", age: 23 }]
}*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 34. 返回数组的中位数
// 当数组的元素个数为奇数时,排序数组的中值就是中间的元素。当数组元素个数为偶数时,中值取中间两个元素的平均值。
const median = (arr) => {
const sorted = arr.sort()
const middle = Math.floor(sorted.length / 2)
return sorted.length % 2 === 0 ? (sorted[middle - 1] + sorted[middle]) / 2 : sorted[middle]
}
2
3
4
5
6
使用方式
const oddArr = [3, 1, 4, 2, 5]
console.log(median(oddArr)) // 3
const evenArr = [1, 2, 5, 6]
console.log(median(evenArr)) // 3.5
2
3
4
# 35. 返回数组中重复出现次数最多的值
const mode = (arr) => {
const counts = arr.reduce((acc, curr) => ({ ...acc, [curr]: (acc[curr] || 0) + 1 }), {})
const maxCount = Math.max(...Object.values(counts))
return Object.keys(counts)
.filter((key) => counts[key] === maxCount)
.map(Number)
}
2
3
4
5
6
7
使用方式
const arr1 = [1, 2, 3, 2, 4, 2, 5]
console.log(mode(arr1)) // [2]
const arr2 = [1, 2, 3, 2, 4, 4, 5]
console.log(mode(arr2)) // [2, 4]
2
3
4
# 36. 使用扩展运算符和 Array.from 创建一个长度为 n 的数组
const createArr = (n) => [...Array.from({ length: n }, (_, index) => index)]
使用方式
console.log(createArr(5)) // [ 0, 1, 2, 3, 4 ]
# 37. 使用解构获取数组的最后一个元素
/*
在 last() 函数内部,展开运算符 (...) 用于创建原始数组的副本。这是必需的,因为 pop() 方法会修改原始数组并返回删除的元素。
然后,对数组的副本调用 pop() 方法,删除并返回数组的最后一个元素。由于在调用 pop() 方法之前复制了数组,因此不会修改原始数组。
*/
const last = (arr) => [...arr].pop()
2
3
4
5
使用方式
const people = [
{ name: 'Alice', age: 21 },
{ name: 'Bob', age: 22 },
{ name: 'Charlie', age: 21 },
{ name: 'David', age: 23 },
{ name: 'Eve', age: 22 },
]
console.log(last(people)) // { name: "Eve", age: 22 }
2
3
4
5
6
7
8
# 38. 使用布尔构造函数检查变量是否为真
const isTruthy = (val) => Boolean(val)
使用方式
console.log(isTruthy(false)) // false
console.log(isTruthy(0)) // false
console.log(isTruthy(-0)) // false
console.log(isTruthy(0n)) // false
console.log(isTruthy('')) // false
console.log(isTruthy(null)) // false
console.log(isTruthy(undefined)) // false
console.log(isTruthy(NaN)) // false
console.log(isTruthy(true)) // true
console.log(isTruthy({})) // true
console.log(isTruthy([])) // true
console.log(isTruthy(42)) // true
console.log(isTruthy('0')) // true
console.log(isTruthy('false')) // true
console.log(isTruthy(new Date())) // true
console.log(isTruthy(Infinity)) // true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 39. 从数组中删除虚假值
const compact = (arr) => arr.filter(Boolean)
使用方式
const falsyArr = [false, 0, -0, 0n, '', null, undefined, NaN]
const mixArr = [true, false, {}, 0, [], '', '0', null, 'false', undefined, 42]
console.log(compact(falsyArr)) // []
console.log(compact(mixArr)) // [ true, {}, [], '0', 'false', 42 ]
2
3
4
# 40. 将字符串数组转换为数字
const toNumbers = (arr) => arr.map(Number)
使用方式
const strArr = ['1', '2', '3', '4', '5']
console.log(toNumbers(strArr)) // [ 1, 2, 3, 4, 5 ]
2
# 41. 返回一个键值翻转的对象
/*
Object.entries() 方法用于从原始对象创建键值对数组。然后使用 map() 方法迭代数组中的每个键值对,并返回一个新的翻转键值对数组。最后,使用 Object.fromEntries() 方法从翻转的键值对数组中创建一个新对象。
*/
const flip = (obj) => Object.fromEntries(Object.entries(obj).map(([key, value]) => [value, key]))
2
3
4
使用方式
const myDog = {
firstName: 'oscar',
lastName: 'king',
age: 3,
}
console.log(flip(myDog)) // { 3: 'age', oscar: 'firstName', king: 'lastName' }
2
3
4
5
6
# 42. 返回一个指定 key 的对象
/*
在 pick() 函数内部,对 obj 参数调用 Object.entries() 方法以创建键值对数组。然后,在键值对数组上调用 filter() 方法。对于每个键值对,filter() 方法解构键变量,如果键数组包含键则返回 true。这将创建一个过滤后的键值对数组。
最后,在过滤后的键值对数组上调用 Object.fromEntries() 方法来创建一个仅包含指定键及其对应值的新对象。pick() 函数返回新对象。
*/
const pick = (obj, keys) => {
if (!obj || !keys?.length) return {}
return Object.fromEntries(Object.entries(obj).filter(([key]) => keys.includes(key)))
}
2
3
4
5
6
7
8
使用方式
const myDog = {
firstName: 'oscar',
lastName: 'king',
age: 3,
}
console.log(pick(myDog, [])) // {}
console.log(pick(myDog, ['firstName'])) // { firstName: 'oscar' }
console.log(pick(myDog, ['firstName', 'lastName'])) // { firstName: 'oscar', lastName: 'king' }
2
3
4
5
6
7
8
# 43. 返回一个对 value 去重的对象
/*
它首先在输入对象上调用 Object.entries() 方法以获取键值对数组。然后,它使用 filter() 方法过滤条目数组,并仅返回值唯一的条目。
要检查一个值是否唯一,它在原始条目数组上使用 findIndex() 方法。它查找与当前过滤的条目具有相同值的第一个条目的索引。如果当前条目的索引等于第一个匹配条目的索引,则意味着该值是唯一的,应该包含在结果对象中。
最后,它使用 Object.fromEntries() 方法将过滤后的条目数组转换回对象。
*/
const uniqueValues = (obj) =>
Object.fromEntries(
Object.entries(obj).filter(
([key, value], index, entries) => entries.findIndex(([k, v]) => v === value) === index,
),
)
2
3
4
5
6
7
8
9
10
11
使用方式
const myDog = {
id: 3,
firstName: 'oscar',
lastName: 'oscar',
age: 3,
}
console.log(uniqueValues(myDog)) // { id: 3, firstName: 'oscar' }
2
3
4
5
6
7