问题处理和代码片段
sunshj Lv4

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

问题

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
2
<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
debounceBtn.onclick = debounce(() => {
console.log("防抖")
}, 3000)

throttleBtn.onclick = 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 myTypeOf(data) {
return Object.prototype.toString.call(data).slice(8, -1)
}

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

1
2
3
4
function charCount(str) {
const count = [...str].reduce((p, v) => (p[v]++ || (p[v] = 1), p), {})
return Object.fromEntries(Object.entries(count).sort((a, b) => b[1] - a[1]))
}

对象数组去重

1
2
3
4
function uniqueObjInArray(arr, keyStr) {
const map = new Map();
return arr.filter(v => !map.has(v[keyStr]) && map.set(v[keyStr], 1));
}

深拷贝

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 (val.hasOwnProperty(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
class EventBus {
constructor() {
this.events = new Map()
}

on(name, callback) {
if (!this.events.has(name)) {
this.events.set(name, [])
}
this.events.get(name).push(callback)
}

emit(name, ...args) {
if (this.events.has(name)) {
this.events.get(name).forEach(cb => cb(...args))
}
}

off(name, callback) {
if (this.events.has(name)) {
if (callback) this.events.set(name, this.events.get(name).filter(e => e !== callback))
else this.events.delete(name)
}
}

once(name, callback) {
let once = (...args) => {
callback(...args)
this.off(name, once)
}
this.on(name, once)
}
}

// 测试
const bus = new EventBus()

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

function handleEvt2(data) {
console.log("evt2 triggered", data)
}

function handleEvt3(data) {
console.log("evt3 triggered", data)
}

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

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

bus.emit("evt2", "ohmygod")

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 : 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
function arrayToTree<T extends object, K extends keyof T, P extends keyof T>(arr: T[], key: K, parentKey: P) {
type Res = T & { children?: Res[] }
const MAP = new Map<T[K] | T[P], Res>()
const result: Res[] = []

for (const item of arr) {
const id = item[key]
const parentId = item[parentKey]
const parent = MAP.get(parentId)

if (parent) {
if (!parent.children) parent.children = []
parent.children.push(item)
} else {
result.push(item)
}
MAP.set(id, item)
}

return result
}

可控制状态的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
}
}

排序算法

冒泡排序

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
let 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
43
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 (let i = 0; i < arr.length; i++) {
const index = Math.floor((arr[i] - min) / bucketSize)
buckets[index].push(arr[i])
}
return buckets
}

function sortBuckets(arr) {
const sorted = []
for (let i = 0; i < arr.length; i++) {
if (arr[i]) {
insertionSort(arr[i])
sorted.push(...arr[i])
}
}
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 = [...new Array(base)].map(() => [])
for (let 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 (let i = 0; i < arr.length; i++) {
if (arr[i] === 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

let 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
// 需要传入有序数组
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

let 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
}

随机算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function shuffle(arr) {
for (let i = arr.length - 1; i > 0; i--) {
const randIndex = Math.floor(Math.random() * (i + 1))
swap(arr, i, randIndex)
}
return arr
}


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