- 防抖-连续触发事件只有最后一次触发
function debounce(fn, time) {
let timer;
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
clearTimeout(timer);
timer = null;
}, time);
};
}
- 节流-会有冻结时间,连续触发事件在冻结时间内触发的事件无法被触发
function throttle(fn, time) {
let timer;
return function () {
if (timer) return;
timer = setTimeout(() => {
fn.apply(this, arguments);
clearTimeout(timer);
timer = null;
}, time);
};
}
- promise相关方法封装
const myProiseAll = (promises) => {
let count = 0;
let arr = [];
return new Promise((res, rej) => {
promises.forEach((ele, ind) => {
Promise.resolve(ele)
.then((i) => {
arr[ind] = i;
count += 1;
if (count === promises.length) {
res(arr);
}
})
.catch(rej);
});
});
};
const myPromiseRace = (promises) => {
return new Promise((res, rej) => {
promises.forEach((i) => {
Promise.resolve(i).then(res, rej);
});
});
};
const myPromiseAny = (promises) => {
let count = 0;
let arr = [];
return new Promise((res, rej) => {
promises.forEach((i, ind) => {
Promise.resolve(i).then(res, (err) => {
arr[ind] = { val: err };
count += 1;
if (count === promises.length) {
rej(new Error('........'));
}
});
});
});
};
const allSettled = (promises) => {
return new Promise((resolve) => {
const data: any = [],
len = promises.length;
let count = len;
for (let i = 0; i < len; i += 1) {
const promise = promises[i];
promise
.then(
(res) => {
data[i] = res;
},
(error) => {
data[i] = error;
}
)
.finally(() => {
// promise has been settled
if (!--count) {
resolve(data);
}
});
}
});
};
- 数组reduce方法封装
Array.prototype.reduce = function (
callback: (
previousValue: U,
currentValue: T,
index: number,
array: T[]
) => U,
initialValue?: U
): U | T {
const array = this as T[];
let result: T | U;
let startIndex = 0;
if (initialValue !== undefined) {
result = initialValue;
} else {
if (array.length === 0) {
throw new TypeError('Reduce of empty array with no initial value');
}
result = array[0];
startIndex = 1;
}
// 迭代数组
for (let i = startIndex; i < array.length; i++) {
result = callback(result as U, array[i], i, this);
}
return result;
};
- 批量加载图片
let urls = [
'https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg',
'https://www.kkkk1000.com/images/getImgData/gray.gif',
'https://www.kkkk1000.com/images/getImgData/Particle.gif',
'https://www.kkkk1000.com/images/getImgData/arithmetic.png',
'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif',
'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg',
'https://www.kkkk1000.com/images/getImgData/arithmetic.gif',
'https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/10/29/166be40ccc434be0~tplv-t2oaga2asx-image.image',
];
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = function () {
console.log('一张图片加载完成');
document.body.append(img);
resolve();
};
img.onerror = reject;
img.src = url;
});
}
async function limitLoad(urls, loadImg, limit) {
// 复制urls数组
const sequence = [...urls];
// 存储正在执行的promise
const promises = new Array(limit).fill(null);
// 初始化:先并发执行limit个任务
for (let i = 0; i < limit; i++) {
if (sequence.length) {
promises[i] = loadImg(sequence.shift());
}
}
// 处理剩余的任务
for (const url of sequence) {
// 等待最快的任务完成
const fastestIndex = await Promise.race(
promises.map((promise, index) => promise.then(() => index))
);
// 替换已完成的任务位置
promises[fastestIndex] = loadImg(url);
}
// 等待所有任务完成
return Promise.all(promises);
}
limitLoad(urls, loadImg, 3)
.then((res) => {
console.log('图片全部加载完毕');
console.log(res);
})
.catch((err) => {
console.error('err==>', err);
});
- 去除字符串空格
const str = ' H e l l o , W o r l d ! ';
const noSpacesStr = str.split('').filter((char) => char !== ' ').join('');
- 深度拷贝对象
const obj = {
arr: [],
a: 1,
};
obj.sub = obj;
obj.arr.push(obj);
function deepCopy(value, map = new Map()) {
if (value === null || typeof value !== 'object') {
return value;
}
if (map.has(value)) {
return map.get(value);
}
const res = Array.isArray(value) ? [] : {};
map.set(value, res);
for (let key in value) {
res[key] = _deepCopy(value[key]);
}
return res;
}
deepCopy(obj);
- 判断多维数组嵌套层级
const arr = [1,[2,[3,4,[5,6]]]]
//方法一
const getMaxDepth = (arr)=>{
if(!Array.isArray(arr)){
return 0;
}
if(arr.length === 0){
return 0;
}
let maxChildDepth = 0;
for(const item of arr) {
const childDepth = getMaxDepth(item)
maxChildDepth = Math.max(maxChildDepth,childDepth)
}
return maxChildDepth+=1
}
//方法二
const getMaxDepth = (arr) => {
const res = [];
const loop = (item, ind) => {
res[ind] = (res[ind] || 0) + 1;
item.forEach((i) => {
if (Array.isArray(i)) {
loop(i, ind);
}
});
};
arr.forEach((item, ind) => {
if (Array.isArray(item)) {
loop(item, ind);
} else {
res[ind] = 0;
}
});
const num = Math.max(...res) + 1;
return num;
};
}
- 俩数之和
// 示例 const nums = [2, 7, 11, 15]; const target = 9; 输出0和1
const twoSum = (num,target) => {
const obj = {};
for(let i=0;i {
let maxArr = [];
let stempArr = [];
let i = 0;
while (i < str.length) {
const char = str[i];
if (!stempArr.includes(char)) {
stempArr.push(char);
i++;
} else {
if (maxArr.length < stempArr.length) {
maxArr = [...stempArr];
}
// 找到重复字符的位置并调整窗口
const duplicateIndex = stempArr.indexOf(char);
stempArr = stempArr.slice(duplicateIndex + 1);
stempArr.push(char);
i++;
}
}
if (maxArr.length < stempArr.length) {
maxArr = [...stempArr];
}
console.log('maxArr==', maxArr.join(''));
return maxArr.join('');
};
lengthOfLongestSubstring('abcadbecf')
//方法二:双指针
const lengthOfLongestSubstring = (str) => {
let left = 0,
right = 0,
maxLngth = 0;
let map = new Map();
while (right <= str.length) {
const char = str[right];
if (map.has(char)) {
left = map.get(char) + 1;
}
map.set(char, right);
maxLngth = Math.max(right - left, maxLngth);
right++;
}
console.log(maxLngth);
};
- 数组排序
//冒泡排序
const lengthOfLongestSubstring = (arr) => {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
};
const arr = [2, 1, 5, 3, 23, 2, 3, 6, 9];
lengthOfLongestSubstring(arr);
//选择排序
const arr = [1, 2, 19, 12, 21, 13, 8, 4];
const fn = (arr) => {
for (let i = 0; i < arr.length; i++) {
let min = i;
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[min]) {
min = j;
}
}
[arr[i], arr[min]] = [arr[min], arr[i]];
}
return arr;
};
const res = fn(arr);
//数组快排
const quickSort = (arr) => {
if (arr.length <= 1) {
return arr;
}
const pivotIndex = Math.floor(arr.length / 2);
const pivot = arr[pivotIndex];
const left = [];
const right = [];
for (let i = 0; i < arr.length; i++) {
if (i === pivotIndex) {
continue;
}
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return [...quickSort(left), pivot, ...quickSort(right)];
};
const arr = quickSort([12, 1, 2, 33, 5, 11, 23, 21]);
//数组原地快排
const quickSort = (arr, start = 0, end = arr.length - 1) => {
if (start < end) {
const midInd = getMidInd(arr, start, end);
quickSort(arr, start, midInd - 1);
quickSort(arr, midInd + 1, end);
}
return arr;
};
// 获取中间索引
const getMidInd = (arr, left, right) => {
const lastVal = arr[right]; // 选择最后一个元素作为基准数
let start = left; // 记录小于基准数的元素应该放置的位置
for (let i = left; i < right; i++) {
const element = arr[i];
if (element < lastVal) {
// 将小于基准数的元素交换到左侧区域
[arr[start], arr[i]] = [arr[i], arr[start]];
start++; // 左侧区域边界右移
}
}
// 将基准值放到正确的位置上(左侧小于它的元素和右侧大于等于它的元素之间)
[arr[right], arr[start]] = [arr[start], arr[right]];
return start; // 返回基准值的最终位置
};
console.log(quickSort([2, 33, 41, 23, 13, 53]));
- 链表反转
const lengthOfLongestSubstring = (head) => {
let current = head;
let prev = null;
while (current) {
let tempNext = current.next;
current.next = prev;
prev = current;
current = tempNext;
}
while (prev) {
console.log(prev.val);
prev = prev.next;
}
return prev;
};
const node = {
val: 1,
next: {
val: 2,
next: {
val: 3,
next: {
val: 4,
next: {
val: 5,
next: null,
},
},
},
},
};
lengthOfLongestSubstring(node);
- 找出数组中加和等于目标值的最短数组组合
//例如给定如下一个目标值和一个数组
const target = 7,
numbers = [2, 3, 0, 4, 1, 3];
// 找数组最短组合
// 写个函数输出3,因为[3,0,4]是最短数组组合
const fn = (target, numbers) => {
let resArr = [];
let startInd = 0;
while (startInd < numbers.length) {
let i = startInd;
let tempArr = [];
while (i < numbers.length) {
if (tempArr.reduce((a, b) => a + b, 0) < target) {
tempArr.push(numbers[i]);
}
if (
tempArr.reduce((a, b) => a + b, 0) == target &&
(resArr.length == 0 || resArr.length > tempArr.length)
) {
resArr = tempArr;
break;
} else if (tempArr.reduce((a, b) => a + b, 0) > target) {
break;
}
i++;
}
startInd++;
}
return resArr.length;
};
const a = fn(target, numbers);
console.log(a);
14、数组flat方法
const arr = [1, 2, [5, 6, 7]];
const fn = (arr) => {
const carr = [];
const loop = (arr) => {
arr.forEach((i) => {
if (Array.isArray(i)) {
loop(i);
} else {
carr.push(i);
}
});
};
loop(arr);
console.log('carr==>', carr);
return carr;
};
fn(arr);
15.求字符串最多出现次数的字符
const str = 'abcabbbas';
const fn = (str) => {
const obj = {};
for (let i = 0; i < str.length; i++) {
if (obj[str[i]]) {
obj[str[i]]++;
} else {
obj[str[i]] = 1;
}
}
const max = {
val: '',
count: 0,
};
for (const key in obj) {
if (obj[key] > max.count) {
max.count = obj[key];
max.val = key;
}
}
console.log(max);
return max.count;
};
fn(str);
16、实现一个拖拽效果
<div id="content"></div>
const dom = document.getElementById('content');
const body = document.getElementsByTagName('body')[0];
dom.addEventListener('mousedown', (e) => {
const mouseupFn = (e) => {
body.removeEventListener('mousemove', mousemoveFn);
};
const mousemoveFn = (e) => {
const { pageX, pageY } = e;
dom.style.left = pageX - 50 + 'px';
dom.style.top = pageY - 50 + 'px';
dom.addEventListener('mouseup', mouseupFn);
};
body.addEventListener('mousemove', mousemoveFn);
});
.container {
width: 400px;
height: 400px;
border: 2px dashed #ccc;
padding: 20px;
}
.draggable {
width: 100px;
height: 100px;
background: #2196f3;
color: white;
display: flex;
align-items: center;
justify-content: center;
cursor: move;
margin: 10px;
transition: opacity 0.3s;
}
.dragging {
opacity: 1;
}
<div class="container">
<div class="draggable">拖拽元素 1</div>
</div>
// 获取所有可拖拽元素
// const draggables = document.querySelectorAll('.draggable');
const draggable =
document.getElementsByClassName('draggable')[0];
// 绑定拖拽开始事件
draggable.addEventListener('dragstart', handleDragStart);
// 拖拽结束事件处理,清理状态
draggable.addEventListener('dragend', handleDragEnd);
// 容器绑定放置事件
const container = document.querySelector('.container');
// 在目标区域悬停时持续触发,必须阻止默认行为才能触发 drop
container.addEventListener('dragover', handleDragOver);
// 在目标区域释放时触发,处理放置逻辑
container.addEventListener('drop', handleDrop);
// 当前被拖拽的元素
let draggedElement = null;
// 拖拽开始事件处理
function handleDragStart(e) {
draggedElement = this;
this.classList.add('dragging');
// 设置拖拽数据(可传递自定义数据)
e.dataTransfer.setData('text/plain', '传递拖拽数据');
}
// 拖拽悬停事件处理(必须阻止默认)
function handleDragOver(e) {
e.preventDefault(); //必须阻止默认行为才能触发drop事件
}
// 放置事件处理
function handleDrop(e) {
// e.preventDefault();
// 获取拖拽数据
const data = e.dataTransfer.getData('text/plain');
console.log('拖拽数据:', data);
// 计算新位置
const rect = container.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 更新元素位置
draggedElement.style.position = 'absolute';
draggedElement.style.left = x - 50 + 'px'; // 居中偏移
draggedElement.style.top = y - 50 + 'px';
}
// 拖拽结束事件处理
function handleDragEnd() {
this.classList.remove('dragging');
draggedElement = null;
}
17、数组转树
let flatArr = [
{ id: 1, title: '1', pid: 0 },
{ id: 2, title: '2', pid: 0 },
{ id: 3, title: '2-1', pid: 2 },
{ id: 4, title: '3-1', pid: 3 },
{ id: 5, title: '4-1', pid: 4 },
{ id: 6, title: '2-2', pid: 2 },
];
const fn = (flatArr) => {
let map = new Map();
flatArr.forEach((i) => {
map.set(i.id, { ...i, children: [] });
});
let tree = [];
flatArr.forEach((i) => {
let node = map.get(i.id);
let parent = map.get(i.pid);
if (parent) {
parent.children.push(node);
} else {
tree.push(node);
}
});
console.log('tree:', tree, '====', JSON.stringify(tree));
};
fn(flatArr);
18、斐波拉契数列(爬楼梯,每次爬1个或者2个)
function fn(num) {
if(num<=2) return 1;
return fn(num-1)+fn(num-2);
}
--------
const fn = (n) => {
if (n < 2) {
return n;
}
let arr = [0, 1];
for (let i = 2; i <= n; i++) {
arr[i] = arr[i - 1] + arr[i - 2];
}
return arr[n];
};
19、查找对象属性
const obj = {
a: {
b: {
c: 1,
},
d: 2,
},
};
const path = 'a,d';
const fn = (obj, path) => {
let res = obj;
const arr = path.split(',');
let i = 0;
while (i < arr.length) {
const key = arr[i];
res = res[key];
i++;
}
return res;
};
console.log(fn(obj, path));
20、多个函数串行执行
const fns = [() => {}, () => {}, () => {}];
const fn = async (arr, params) => {
let res = params;
for (let i = 0; i < arr.length; i++) {
res = await arr[i](res);
}
return res;
};
21、二分法取数
const arr = [1, 2, 3, 4, 5, 6, 7];
const fn = (target) => {
let start = 0,
end = arr.length - 1;
while (start <= end) {
let mindInd = Math.floor((end + start) / 2);
let minVal = arr[mindInd];
if (target < minVal) {
end = mindInd;
} else if (target > minVal) {
start = mindInd;
} else {
return mindInd;
}
}
};
console.log(fn(2));
22、苛里化
题目一
const sum = (a, b, c) => {
return a + b + c;
};
const aaa = fn(sum);
const res = aaa(1, 2, 3);
const res1 = aaa(1, 2)(3);
const res2 = aaa(1)(4, 3);
// 实现fn函数
function fn(sum) {
return function (...args) {
if (args.length >= sum.length) {
// 如果参数数量足够,直接执行 sum 函数
return sum(...args);
} else {
// 否则返回一个新的函数,等待更多的参数
return function (...nextArgs) {
return fn(sum)(...args, ...nextArgs);
};
}
};
}
题目二
// sum(1, 2, 3).sumOf(); //6
// sum(2, 3)(2).sumOf(); //7
// sum(1)(2)(3)(4).sumOf(); //10
// sum(2)(4, 1)(2).sumOf(); //9
// 实现sum函数
const sum = (...params)=>{
const fn = (...args)=>{
return sum(...params, ...args)
}
fn.sumOf = ()=>reduce((a,b)=>a+b,0)
return fn;
}
23、括号匹配判断
function isParenthesesMatched(str) {
const stack = [];
for (let i = 0; i < str.length; i++) {
const c = str[i];
if (c === '(' || c === '{' || c === '[') {
stack.push(c);
} else if (c === ')' || c === '}' || c === ']') {
const top = stack[stack.length - 1];
if ((top === '(' && c === ')') || (top === '{' && c === '}') || (top === '[' && c === ']')) {
stack.pop();
} else {
return false;
}
}
}
return stack.length === 0;
}
console.log(isParenthesesMatched('{}[]()[}'));
24、十进制二进制转换
//思路:除2取余,将余数拼接在结果字符串的开头,然后商除以2再取余,重复此过程,直到商为0
// 十进制转二进制
function toBinary(num) {
if (num === 0) {
return '0';
}
let result = '';
while (num > 0) {
result = (num % 2) + result;
num = Math.floor(num / 2);
}
return result;
}
console.log(toBinary(10));
//思路:从高位开始,每次取二进制数末尾的数,然后乘以2的幂次方,幂次从后往前递增,并求和
// 二进制转十进制
function toDecimal(binary) {
let result = 0;
let str = `${binary}`;
for (let i = str.length - 1; i >= 0; i--) {
result += parseInt(str[i]) * Math.pow(2, str.length - 1 - i);
}
console.log('>>>', result);
return result;
}
25、数组拉平
var arr = [1, { a: 2 }, [3, [4, 5]]];
function flatDeep(arr) {
let carr = [];
const loop = (arr) => {
arr.forEach((ele) => {
if (Array.isArray(ele)) {
loop(ele);
} else {
carr.push(ele);
}
});
};
loop(arr);
console.log("carr", carr);
return carr;
}
const newArr = flatDeep(arr); // [1, {a: 2}, 3, 4, 5]
26、找数组最短组合值
// 找出数组中数值的加和大于等于目标值的连续的最小数组长度
const s = 7,
nums = [2, 3, 1, 4, 1, 3];
//写个函数输出3,因为[3,1,4]是最短数组组合
const fn = (s,nums)=>{
let num = nums.length
let tempArr = []
let startInd = 0;
const loop = (ind = 0) => {
for(let i = ind; i < nums.length; i++){
tempArr.push(nums[i])
const res = tempArr.reduce((a,b)=>a+b)
if(res>s){
console.log('tempArr>>>',tempArr)
num = Math.min(num,tempArr.length)
tempArr = []
loop(startInd++)
}
}
}
loop(startInd)
return num
}
fn(s,nums)
27、找数组最贴近入参的值
const arr = [1, 9, 3, 33, 50, 86, 70];
//比如传入10,返回9
//传入60,返回70(偏大的值)
const fn = (arr, n) => {
const newArr = arr.map((i) => Math.abs(i - n));
const num = Math.min(...newArr);
let indArr = [];
newArr.forEach((i, ind) => {
if (i === num) {
indArr.push(ind);
}
});
const resArr = indArr.map((i) => arr[i]);
return Math.max(...resArr);
};
console.log(fn(arr, 60));
28、重复请求一个接口,有一次成功就返回
// 方法一
const reruest = (url,maxCount = 3) => {
return fetch(url).catch((err) =>
maxCount <= 0 ? Promise.reject(err) : reruest(url, maxCount--)
)
}
// 方法二
const reTry = (url) => {
return new Promise((resolve, reject) => {
let count = 0;
const request = () => {
axios.get(url).then(resolve).catch(err => {
count++;
if (count < 3) {
request();
} else {
reject(err);
}
})
};
});
};
29、任务并发控制
// 已知如下代码 实现SuperTask
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 创建一个超时任务类
const superTask = new SuperTask()
function addTask(time,name){
superTask.add(()=>timeout(time)).then(()=>{
console.log('任务' + name + '执行完成')
})
}
addTask(10000,'任务1') // 10000毫秒后输出 '任务1执行完成'
addTask(5000,'任务2') // 5000毫秒后输出 '任务2执行完成'
addTask(3000,'任务3') // 3000毫秒后输出 '任务3执行完成'
addTask(4000,'任务4') // 12000毫秒后输出 '任务4执行完成'
addTask(5000,'任务5') // 15000毫秒后输出 '任务5执行完成'
// 从打印可以看出,任务并发数是2
class SuperTask {
constructor(limit = 2) {
this.limit = limit; // 并发任务数
this.queue = []; // 任务队列
this.workingCount = 0; // 正在处理的任务数
}
add(task){
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.run();
});
}
run(){
while(this.queue.length && this.workingCount < this.limit){
const { task, resolve, reject } = this.queue.shift();
this.workingCount++;
task().then(resolve, reject).finally(() => {
this.workingCount--;
this.run();
});
}
}
}
30、树转数组
如下代码
const tree = [ { value: '1', label: '1', children: [ { value: '1-1', label: '1-1', children: [ { value: '1-1-1', label: '1-1-1' }, { value: '1-1-2', label: '1-1-2' } ]
}
]
},
{
value: '2',
label: '2',
children: [
{
value: '2-1',
label: '2-1'
},
{
value: '2-2',
label: '2-2'
}
]
}
]
const arr = treeToArr(tree);
输出 [ [ '1', '1-1', '1-1-1' ],
[ '1', '1-1', '1-1-2' ],
[ '2', '2-1' ],
[ '2', '2-2' ]
]
const treeToArr = (tree)=>{
const arr = [];
const loop = (tree, path=[])=>{
tree.forEach(i=>{
const tempPath = [...path, i.value]
if(i.children?.length){
loop(i.children, tempPath)
}else{
arr.push(tempPath)
}
})
}
loop(tree)
return arr;
}
31、多任务开始暂停操作
// 有俩按钮,支持任务的开始和暂停,任务执行完返回所有任务的执行结果
开始
暂停
const runTasks = (...tasks)=>{
let isRunning = false
const res = [] //结果
let index = 0
const start = () => {
return new Promise(async (resolve)=>{
if(isRunning){
return
}
isRunning = true
while (index < tasks.length) {
const ele = await tasks[index]();
res.push(ele)
index++
if(!isRunning){
return
}
}
isRunning = false
return resolve(res)
})
}
const stop = () => {
isRunning = false
}
return {
start,
stop
}
}
const arr = []
for(let i=1;i{
console.log(`任务${i}开始执行`)
return new Promise((resolve)=>{
setTimeout(()=>{
resolve(i)
console.log(`任务${i}执行结束`)
}, 3000)
})
})
}
const {start,stop} = runTasks(...arr)
const startEvent = async () => {
console.log('点击了开始按钮')
const data = await start()
console.log('data==>', data)
}
const stopEvent = () => {
stop()
}
32、二叉树遍历
const treeNode = {
val: 1,
left: {
val: 2,
left: {
val: 4,
left: null,
right: null
},
right: {
val: 5,
left: null,
right: null
}
},
right: {
val: 3,
left: {
val: 6,
left: null,
right: null
},
right: {
val: 7,
left: null,
right: null
}
}
}
const getTreeArr = (root) => {
const arr = []
const _fn = (root)=>{
if(!root) return
arr.push(root.val)
_fn(root.left)
_fn(root.right)
}
_fn(root)
return arr
}
const a = getTreeArr(treeNode)
console.log('7890>>>',a)