问题处理和代码片段
sunshj Lv3

本篇持续收集日常遇到的问题的解决方案,以及一些常用的代码片段

问题

1、git clone 换行符问题

git clone 过程,git 将所有文件换行符(LF)更换成本地换行符(CRLF)了,而 eslint 检查工具要求换行符为 LF

解决方法

在任意位置的命令行窗口中输入以下命令:

1
2
git config --global core.autocrlf false
git config --global core.eol lf

将 git 的 autocrlf 功能设置为 false,并把 eol(end of line)设置为 lf,再重新 clone 一下代码就可以啦

2、ES6 对象简写

之前写对象里的方法一直使用的是 getName:()=>{...}的形式,后来感觉不好看,想要改成 getName(){...}的形式,但是手动修改是不可能的,可以使用正则进行全局替换。

1
2
3
4
# 匹配字符
(\w+)\: ?(async)? \((.*)\) \=\>
# 替换为
$2 $1\($3\)

代码

防抖与节流

1
<button id="debounceBtn">防抖Click</button> <button id="throttleBtn">节流Click</button>
1
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
debounceBtn.addEventListener(
'click',
debounce(() => {
console.log('防抖')
}, 3000)
)

throttleBtn.addEventListener(
'click',
throttle(() => {
console.log('节流')
}, 3000)
)

function debounce(fn, wait = 2000) {
let timer = null
return function (...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(this, args)
}, wait)
}
}

function throttle(fn, delay = 2000) {
let prevTime = 0
return function (...args) {
const nowTime = Date.now()
if (nowTime - prevTime > delay) {
prevTime = nowTime
fn.apply(this, args)
}
}
}

判断类型

1
2
3
function getTypeOf(data) {
return Object.prototype.toString.call(data).slice(8, -1)
}

统计字符串中每个字符出现的次数

1
2
3
function charCount(str) {
return [...str].reduce((p, v) => (p[v]++ || (p[v] = 1), p), {})
}

对象数组去重 1

1
2
3
4
function uniqueObjectInArray(array, keyStr) {
const keys = new Set()
return array.filter(v => !keys.has(v[keyStr]) && keys.add(v[keyStr]))
}

对象数组去重 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function unique<T, K extends keyof T>(array: T[], getKey?: K | ((item: T) => T[K])) {
const result: T[] = []
const keys = new Set()

array.forEach(item => {
const key = getKey ? (typeof getKey === 'function' ? getKey(item) : item[getKey]) : item
if (!keys.has(key)) {
keys.add(key)
result.push(item)
}
})

return result
}

深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function deepClone(value) {
const cache = new WeakMap()
function _clone(val) {
if (val === null || typeof val !== 'object') return val
if (cache.get(val)) return cache.get(val)
const result = Array.isArray(val) ? [] : {}
cache.set(val, result)
for (const key in val) {
if (Reflect.has(val, key)) {
result[key] = _clone(val[key])
}
}
return result
}

return _clone(value)
}

EventBus

1
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class EventBus<T extends Record<string, (...args: any[]) => void>> {
private events: Map<keyof T, Set<T[keyof T]>>

constructor() {
this.events = new Map()
}

on<K extends keyof T>(name: K, callback: T[K]) {
if (!this.events.has(name)) {
this.events.set(name, new Set())
}
this.events.get(name)?.add(callback)
}

emit<K extends keyof T>(name: K, ...args: Parameters<T[K]>) {
if (this.events.has(name)) {
this.events.get(name)?.forEach(cb => cb(...args))
}
}

off<K extends keyof T>(name: K, callback?: T[K]) {
if (this.events.has(name)) {
if (callback) this.events.get(name)?.delete(callback)
else this.events.delete(name)
}
}

once<K extends keyof T>(name: K, callback: T[K]) {
const once = (...args: any[]) => {
callback(...args)
this.off(name, once as T[K])
}
this.on(name, once as T[K])
}
}

// 测试
type Events = {
evt1: () => void
evt2: (data: number) => void
evt3: (data: string) => void
}

const bus = new EventBus<Events>()

function handleEvt1() {
console.log('evt1 triggered')
}

function handleEvt2(data: number) {
console.log('evt2 triggered', data)
}

function handleEvt3(data: string) {
console.log('evt3 triggered', data)
}

bus.on('evt1', () => {
console.log('evt1 triggered 2')
})

bus.on('evt1', handleEvt1)
bus.on('evt2', handleEvt2)
bus.once('evt3', handleEvt3)

bus.off('evt1', handleEvt1)
bus.emit('evt1')

bus.emit('evt2', 123)

bus.emit('evt3', '执行一次')
bus.emit('evt3', '再执行一次')

// 打印结果
// evt1 triggered 2
// evt2 triggered 123
// evt3 triggered 执行一次

获取嵌套对象中指定路径的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function getValue(obj, path) {
const paths = Array.isArray(path) ? path : path.split('.')

return paths.reduce((current, key) => {
return current && current[key] !== undefined ? current[key] : undefined
}, obj)
}

// 测试
const obj = {
a: {
b: {
c: 113,
d: null
}
}
}
console.log(getValue(obj, 'a.b.c')) // 输出:113
console.log(getValue(obj, ['a', 'b'])) // 输出:{ c: 113, d: null }

手写 call

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Function.prototype.myCall = function (ctx, ...args) {
ctx = ctx === null || ctx === undefined ? globalThis : new Object(ctx)
const key = Symbol('fn')
Object.defineProperty(ctx, key, {
enumerable: false,
value: this
})
const result = ctx[key](...args)
delete ctx[key]
return result
}

// 测试
const obj = {
test(a, b) {
console.log(this)
return a + b
}
}

obj.test.myCall(obj, 1, 2)

数组转树形结构

1
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
type TreeNode<T> = T & { children: Array<TreeNode<T>> }

interface BuildTreeOptions<T> {
key: keyof T
parentKey: keyof T
}

function buildTree<T extends object>(array: T[], options: BuildTreeOptions<T>) {
const { key, parentKey } = options
const tree: Array<TreeNode<T>> = []
const lookup = new Map<T[keyof T], TreeNode<T>>()

for (const item of array) {
lookup.set(item[key], { ...item, children: [] })
}

for (const item of array) {
if (item[parentKey]) {
const parent = lookup.get(item[parentKey])
if (parent) {
parent.children.push(lookup.get(item[key]) as TreeNode<T>)
}
} else {
tree.push(lookup.get(item[key]) as TreeNode<T>)
}
}

return tree
}

统计树形结构的节点数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type TreeNode<T> = T & { children: Array<TreeNode<T>> }

function countTreeNodes<T>(nodes: Array<TreeNode<T>>) {
let count = 0

const traverse = (node: TreeNode<T>) => {
count++
for (const child of node.children) {
traverse(child)
}
}

for (const node of nodes) {
traverse(node)
}

return count
}

可控制状态的 Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function createControllablePromise<T>(promise: Promise<T>) {
let complete: undefined | ((value: T) => void)
let cancel: undefined | ((reason?: any) => void)
const controllablePromise = new Promise<T>((resolve, reject) => {
complete = resolve
cancel = reject
})

return {
promise: () => Promise.race([promise, controllablePromise]),
complete,
cancel
}
}

文字转语音(WebAPI)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function speak(text) {
const utt = new SpeechSynthesisUtterance()
utt.text = text
utt.lang = 'zh-CN'

window.speechSynthesis.speak(utt)

const aliveSpeechInterval = setInterval(() => {
if (!speechSynthesis.speaking) {
clearInterval(aliveSpeechInterval)
} else {
speechSynthesis.pause()
speechSynthesis.resume()
}
}, 14000)
}

随机算法

1
2
3
4
5
6
7
function shuffleArray(arr) {
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1))
;[arr[i], arr[j]] = [arr[j], arr[i]]
}
return arr
}

排序算法

冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function bubbleSort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
let flag = true
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
flag = false
swap(arr, j, j + 1)
}
}
if (flag) break
}
return arr
}

function swap(arr, a, b) {
const tmp = arr[a]
arr[a] = arr[b]
arr[b] = tmp
}

选择排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function selectionSort(arr) {
let min
for (let i = 0; i < arr.length - 1; i++) {
min = i
for (let j = i; j < arr.length; j++) {
if (arr[min] > arr[j]) {
min = j
}
}
if (i !== min) {
swap(arr, i, min)
}
}
return arr
}

function swap(arr, a, b) {
const tmp = arr[a]
arr[a] = arr[b]
arr[b] = tmp
}

插入排序

1
2
3
4
5
6
7
8
9
10
11
12
13
function insertionSort(arr) {
let tmp
for (let i = 1; i < arr.length; i++) {
tmp = arr[i]
let j = i
while (j > 0 && arr[j - 1] > tmp) {
arr[j] = arr[j - 1]
j--
}
arr[j] = tmp
}
return arr
}

归并排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function mergeSort(arr) {
if (arr.length > 1) {
const mid = Math.floor(arr.length / 2)
const left = mergeSort(arr.slice(0, mid))
const right = mergeSort(arr.slice(mid, arr.length))
arr = merge(left, right)
}
return arr
}

function merge(left, right) {
let i = 0
let j = 0
const result = []
while (i < left.length && j < right.length) {
result.push(left[i] < right[j] ? left[i++] : right[j++])
}
return result.concat(i < left.length ? left.slice(i) : right.slice(j))
}

快速排序

1
2
3
4
5
6
7
8
function quickSort(arr) {
if (arr.length < 2) return arr
const base = arr[0]
const minArr = arr.slice(1).filter(v => v <= base)
const maxArr = arr.slice(1).filter(v => v > base)

return [...quickSort(minArr), base, ...quickSort(maxArr)]
}

计数排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function countingSort(arr) {
if (arr.length < 2) return arr
const max = Math.max(...arr)
const counts = new Array(max + 1)
arr.forEach(item => {
if (!counts[item]) {
counts[item] = 0
}
counts[item]++
})

let sortIndex = 0
const newArr = []
counts.forEach((item, index) => {
while (item > 0) {
newArr[sortIndex++] = index
item--
}
})
return newArr
}

桶排序

1
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
37
38
39
40
41
42
function bucketSort(arr, bucketSize = 3) {
if (arr.length < 2) return arr
const buckets = createBucket(arr, bucketSize)
return sortBuckets(buckets)
}

function createBucket(arr, bucketSize) {
const min = Math.min(...arr)
const max = Math.max(...arr)
const bucketCount = Math.floor((max - min) / bucketSize) + 1
const buckets = [...new Array(bucketCount)].map(() => [])

for (const element of arr) {
const index = Math.floor((element - min) / bucketSize)
buckets[index].push(element)
}
return buckets
}

function sortBuckets(arr) {
const sorted = []
for (const element of arr) {
if (element) {
insertionSort(element)
sorted.push(...element)
}
}
return sorted
}

function insertionSort(arr) {
let tmp
for (let i = 1; i < arr.length; i++) {
tmp = arr[i]
let j = i
while (j > 0 && arr[j - 1] > tmp) {
arr[j] = arr[j - 1]
j--
}
arr[j] = tmp
}
}

基数排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function radixSort(arr) {
const base = 10
let divider = 1
const max = Math.max(...arr)

while (divider <= max) {
const buckets = [...Array.from({ length: base })].map(() => [])
for (const val of arr) {
buckets[Math.floor(val / divider) % base].push(val)
}
arr = [].concat(...buckets)
divider *= base
}
return arr
}

搜索算法

顺序搜索

1
2
3
4
5
6
7
function sequentialSearch(arr, target) {
for (const [i, element] of arr.entries()) {
if (element === target) {
return i
}
}
}

二分搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 需要传入有序数组
function binarySearch(arr, target, start, end) {
start = start || 0
end = end || arr.length - 1

if (start <= end && target >= arr[start] && target <= arr[end]) {
if (arr[start] === target) return start
if (arr[end] === target) return end

const mid = Math.ceil((start + end) / 2)
if (arr[mid] === target) return mid
if (arr[mid] > target) return binarySearch(arr, target, start, mid - 1)
return binarySearch(arr, target, mid + 1, end)
}
return -1
}

内插搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 需要传入有序数组
function insertionSearch(arr, target, start, end) {
start = start || 0
end = end || arr.length - 1

if (start <= end && target >= arr[start] && target <= arr[end]) {
if (arr[start] === target) return start
if (arr[end] === target) return end

const mid =
start + Math.floor(((target - arr[start]) / (arr[end] - arr[start])) * (end - start))
if (arr[mid] === target) return mid
if (arr[mid] > target) return insertionSearch(arr, target, start, mid - 1)
return insertionSearch(arr, target, mid + 1, end)
}
return -1
}
 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
访客数 访问量