1. 数组去重
const uniqueArray1 = (arr) => Array.from(new Set(arr))
const uniqueArray2 = (arr) => {
const arrCopy = [...arr]
let len = arrCopy.length
for (let i = 0; i < len - 1; ++i) {
for (let j = i + 1; j < len; ++j) {
if (arrCopy[i] === arrCopy[j]) {
arrCopy.splice(j, 1)
--len
--j
}
}
}
return arrCopy
}
const uniqueArray3 = (arr) => {
const res = []
arr.forEach(item => {
if (!res.includes(item))
res.push(item)
})
return res
}
const uniqueArray4 = (arr) => {
const res = []
arr.forEach(item => {
if (res.indexOf(item) === -1)
res.push(item)
})
return res
}
const uniqueArray5 = (arr) => arr.filter((item, index) => arr.indexOf(item) === index)
const uniqueArray6 = (arr) => arr.reduce((pre, cur) => pre.includes(cur) ? pre: [...pre, cur], [])
const uniqueArray7 = (arr) => {
const res = []
const obj = {}
arr.forEach(item => {
if (!obj[item]){
res.push(item)
obj[item] = true
}
})
return res
}
const uniqueArray8 = (arr) => {
const res = []
const map = new Map()
arr.forEach(item => {
if (!map.has(item)){
res.push(item)
map.set(item, true)
}
})
return res
}
2. 手写flat数组拍平方法(对象拍平)
Array.prototype.myFlat = function(deep=1) {
const inside = (arr, deep) => {
if (deep <= 0) return arr
return arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) ? inside(cur, deep - 1) : cur), [])
}
return inside(this, deep)
}
const obj = {
a: {
b: 1,
c: 2,
d: { e: 5 },
},
b: [1, 3, { a: 2, b: 3 }],
c: 3,
}
// 结果返回如下
// {
// 'a.b': 1,
// 'a.c': 2,
// 'a.d.e': 5,
// 'b[0]': 1,
// 'b[1]': 3,
// 'b[2].a': 2,
// 'b[2].b': 3
// c: 3
// }
const flatten = (obj) => {
const res = {}
const dfs = (obj, str = "") => {
if (typeof obj !== "object") {
res[str] = obj
return
}
if (Array.isArray(obj)) {
for (let i = 0; i < obj.length; ++i) {
dfs(obj[i], str ? str + `[${i}]` : i)
}
} else {
for (const key in obj) {
dfs(obj[key], str ? str + `.${key}` : key)
}
}
}
dfs(obj)
return res
}
console.log(flatten(obj))
3. 排序算法(快排、归并、插入)
快速排序
const quickSort = (arr, low, high) => {
if (low < high) {
const temp = arr[low]
let i = low
let j = high
while (i < j) {
while(i < j && arr[j] >= temp) --j
if (i < j) {
arr[i] = arr[j]
++i
}
while (i < j && arr[i] <= temp) ++i
if (i < j) {
arr[j] = arr[i]
--j
}
}
arr[i] = temp
quickSort(arr, low, i - 1)
quickSort(arr, i + 1, high)
}
}
归并排序
const mergeSort = (arr) => {
if (arr.length <= 1) {
return arr;
}
const middle = Math.floor(arr.length / 2);
const left = arr.slice(0, middle);
const right = arr.slice(middle);
return merge(mergeSort(left), mergeSort(right));
};
const merge = (left, right) => {
let result = [];
let leftIndex = 0;
let rightIndex = 0;
while (leftIndex < left.length && rightIndex < right.length) {
if (left[leftIndex] < right[rightIndex]) {
result.push(left[leftIndex]);
leftIndex++;
} else {
result.push(right[rightIndex]);
rightIndex++;
}
}
return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex));
};
插入排序
const insertSort = (arr) => {
for (let i = 1; i < arr.length; i++) {
let currentVal = arr[i];
for (let j = i - 1; j >= 0 && arr[j] > currentVal; j--) {
arr[j + 1] = arr[j];
}
arr[j + 1] = currentVal;
}
return arr;
};
4. instanceOf
const myInstanceof = (target, type) => {
if (typeof target !== 'object' || target === null) return false
let proto = Object.getPrototypeOf(target)
let prototype = type.prototype
while (true) {
if (proto === null) return false
if (proto === prototype) return true
proto = Object.getPrototypeOf(proto)
}
}
5. 用setTimeout实现setInterval
const mySetInterval = (func, milliseconds) => {
const inside = () => {
func()
setTimeout(() => {
inside()
}, milliseconds);
}
setTimeout(() => {
inside()
}, milliseconds);
}
6. 手写new
function myNew(func, ...agrs) {
/**
* 1. 创建一个空对象obj
* 2. 将对象与构造函数通过原型链连接起来
* 3. 将构造函数的this绑定到obj上
* 4. 根据构造函数的返回类型作判断,如果是原始值,则被忽略,如果是返回对象,则直接将该对象返回给obj
*/
const obj = {}
obj.__proto__ = func.prototype
const res = func.apply(obj, agrs)
return res instanceof Object? res : obj
}
7. 手写call、apply、bind
//call
Function.prototype.myCall = function(obj, ...args) {
obj = obj ? Object(obj) : globalThis
const fn = Symbol('test')
obj[fn] = this
const res = obj[fn](...args)
delete obj[fn]
return res
}
//apply
Function.prototype.myApply = function(obj, args) {
obj = obj ? Object(obj) : globalThis
const fn = Symbol('test')
obj[fn] = this
const res = obj[fn](...args)
delete obj[fn]
return res
}
//bind
Function.prototype.myBind = function(obj, ...args1) {
const fn = this
return function(...args2) {
return fn.apply(obj, args1.concat(args2))
}
}
8. 防抖与节流
// 防抖
function debounce(fn, delay) {
let timer = null;
return function (...args) {
let context = this;
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
};
}
// 节流
function throttle(func, wait) {
let timer;
return function (...args) {
if (timer) return;
timer = setTimeout(() => {
func.apply(this, args);
timer = null;
}, wait);
};
}
9. 手写并发控制
/**
* @param {string[]} urls 待请求的url数组
* @param {number} maxNum 最大并发数
*/
function concurRequset(urls, maxNum) {
return new Promise((resolve) => {
if (urls.length === 0) {
resolve([]);
return;
}
const results = []; //返回的结果,要与urls中一一对应
let index = 0;
let count = 0; // 当前请求完成的数量
// 发送请求
async function request() {
if (index === urls.length) { //index已经到达末尾,结束请求
return;
}
const i = index;
const url = urls[index];
index++;
try {
const resp = await fetch(url);
results[i] = resp;
} catch (err) {
// err加入到results中
results[i] = err;
} finally {
// 判断所有请求都已完成
++count;
if (count === urls.length) {
resolve(results);
}
request();
}
}
const times = Math.min(maxNum, urls.length);
for (let i = 0; i < times; ++i)
request();
})
}
10. 手写promise.all
Promise.prototype.myAll = function (proms) {
return new Promise((resolve, reject) => {
if (!Array.isArray(proms)) {
reject(new Error('params is not array'))
}
let count = 0
const result = []
for (let i = 0; i < proms.length; ++i) {
Promise.resolve(proms[i]).then(value => {
++count
result[i] = value
if (count === proms.length) {
resolve(result)
}
}).catch(e => {
reject(e)
})
}
})
}
11. reduce
Array.prototype.myReduce = function (callback, initVal) {
const arr = this
const len = arr.length
if (typeof callback !== 'function') {
throw new TypeError(callback + 'is not a function')
}
if (len === 0 && !initVal) {
throw new TypeError('Reduce of empty array with no initial value')
}
let curIndex, prevVal
if (initVal) {
prevVal = initVal
curIndex = 0
} else {
prevVal = arr[0]
curIndex = 1
}
for (let i = curIndex; i < len; i++) {
prevVal = callback(prevVal, arr[i], i, arr)
}
return prevVal
}
12. 深拷贝
const cloneDeep = (obj, hash=new WeakMap()) => {
if (obj === null) return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (typeof obj !== 'object') return obj
if (hash.has(obj)) return hash.get(obj)
const newObj = new obj.constructor
hash.set(obj, newObj)
for (let k in obj) {
newObj[k] = cloneDeep(obj[k], hash)
}
return newObj
}
13. EventBus(可以精简一点)
class EventBus {
constructor() {
this.eventBus = {}
}
// 事件监听
$on(eventName, eventCallback, thisArg) {
// 判断事件名是否是string类型
if (typeof eventName !== 'string') {
throw TypeError('传入的事件名数据类型需要设为string类型')
}
// 判断事件函数是否为function类型
if (typeof eventCallback !== 'function') {
throw TypeError('需要传入的回调函数')
}
// 从eventBus中获取对应的事件数据
if (!this.eventBus[eventName]) {
this.eventBus[eventName] = []
}
this.eventBus[eventName].push({ eventCallback, thisArg })
}
// 事件触发
$emit(eventName, ...args) {
if (typeof eventName !== 'string') {
throw TypeError('传入的事件名数据类型需为string类型')
}
// 从eventBus中获取对应的事件数据
const handlers = this.eventBus[eventName] || []
handlers.forEach(handler => {
// 触发事件
handler.eventCallback.apply(handler.thisArg, args)
})
}
// 移除事件监听
$off(eventName, eventCallback) {
// 判断事件类型是否是string类型
if (typeof eventName !== 'string') {
throw TypeError('传入的事件名数据类型需要设为string类型')
}
// 判断事件函数是否为function类型
if (!eventCallback) {
this.eventBus[eventName] = undefined
return
}
if (typeof eventCallback !== 'function') {
throw TypeError('需要传入的回调函数')
}
// 从eventBus中获取对应的事件数据
const handlers = this.eventBus[eventName] || []
// 如果eventName在eventBus中存在则进行操作
if (handlers.length) {
if (eventCallback) {
for (let i = 0; i < handlers.length; ++i) {
if (handlers[i].eventCallback === eventCallback) {
handlers.splice(i, 1)
}
}
}
}
if (handlers.length === 0) {
delete this.eventBus[eventName]
}
}
// 单次监听
$once(eventName, eventCallback, ...thisArg) {
// 判断事件类型是否是string类型
if (typeof eventName !== 'string') {
throw TypeError('传入的事件名数据类型需要设为string类型')
}
// 判断事件函数是否为function类型
if (typeof eventCallback !== 'function') {
throw TypeError('需要传入的回调函数')
}
const handlers = this.eventBus[eventName]
if (handlers.length) {
for (let i = 0; i < handlers.length; ++i) {
if (handlers[i].eventCallback === eventCallback) {
eventCallback.apply(handlers[i].thisArg, thisArg)
this.$off(eventName, eventCallback)
}
}
}
}
}
14. 数字千分位
const reg = /\B(?=(\d{3})+(?!\d))/g
console.log('123456789'.replace(reg, ','))
15. 手写lodash.get
示例:
const obj = {
a: {
b: [0, 1, 2],
c: 2,
}
}
const path = 'a.b[1]'
lodashGet(obj, path) // 输出1
const lodashGet = (obj, path) => path.replace(/\[(\d+)\]/g, '.$1').split('.').reduce((o, k) => o[k], obj)
16. LRU
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) {
return -1;
}
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
put(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
this.cache.delete(this.cache.keys().next().value);
}
this.cache.set(key, value);
}
}
17. Promise实现失败重传
const retry = (promiseFn, times) => {
return new Promise((resolve, reject) => {
const inside = async (times) => {
try {
const res = await promiseFn()
console.log(`成功, ${res}`)
resolve(res)
} catch (err) {
if (times > 0) {
console.log(`倒数第${times}次重试, ${err}`)
--times
inside(times)
} else {
console.log('End');
reject(err)
}
}
}
inside(times)
})
}
const test = () => {
return new Promise((res, rej) => {
const num = Math.random() * 10
num < 5 ? res(`success,${num}`) : rej(`fail,${num}`)
})
}
retry(test, 4).then(res => {
console.log(`res: ${res}`);
}).catch(err => {
console.log(`err: ${err}`);
})
带定时器的失败重传
const retryFn = (task, time, timeout) => {
return Promise.race([retry(task, time), timeEnd(timeout)])
}
const retry = (task, time) => {
return new Promise((resolve, reject) => {
const inside = async (time) => {
try {
const res = await task()
resolve(res)
} catch (error) {
if (time <= 1) {
reject(error)
} else {
inside(time - 1)
}
}
}
inside(time)
})
}
const timeEnd = (timeout) => {
return new Promise(( _ , reject) => {
setTimeout(() => {
reject("timeout error")
}, timeout)
})
}
18 图片懒加载
const images = document.querySelectorAll('img')
const callback = entries => {
entries.forEach(item => {
if (item.isIntersecting) {
const image = item.target
const dataSrc = image.getAttribute('data-src')
image.setAttribute('src', dataSrc)
observer.unobserve(item.target)
}
})
}
const observer = new IntersectionObserver(callback)
images.forEach(image => {
observer.observe(image)
})