1.扁平数据结构转树
let arr = [
{id: 1, name: '部门1', pid: 0},
{id: 2, name: '部门2', pid: 1},
{id: 3, name: '部门3', pid: 1},
{id: 4, name: '部门4', pid: 3},
{id: 5, name: '部门5', pid: 4},
]
// 输出结果:
[
{
"id": 1,
"name": "部门1",
"pid": 0,
"children": [
{
"id": 2,
"name": "部门2",
"pid": 1,
"children": []
},
{
"id": 3,
"name": "部门3",
"pid": 1,
"children": [
// 结果 ,,,
]
}
]
}
]
// 递归的方法
function arrayTotree(arr, result, pid) {
arr.forEach((item) => {
if (item.pid === pid) {
const children = { ...item, children: [] };
result.push(children);
arrayTotree(arr, children.children, item.id);
}
});
};
const result = [];
arrayTotree(arr, result, 0);
console.log(result);
// 非递归的方法 借助对象的 Map
function arrayTotree(arr) {
const result = [];
const map = {};
for (const item of arr) {
const id = item.id;
const pid = item.pid;
if (!map[id]) {
map[id] = { ...item, children: [] };
}
if (pid === 0) {
result.push(map[id]);
} else {
// 容错处理
if (!map[pid]) {
map[pid] = { children: [] };
}
map[pid].children.push(map[id]);
}
}
return result;
};
console.log(arrayTotree(arr));
2.爬楼梯
// 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
// 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
// 动态规划
var climbStairs = function(n) {
if (n <= 2) return n;
const arr = new Array(n).fill(0);
arr[0] = 1;
arr[1] = 2;
for (let i = 2; i < n; i++) {
arr[i] = arr[i - 1] + arr[i - 2];
}
return arr[n - 1]
};
console.log(climbStairs(6));
3.Promise的简单使用(间隔n秒后输出)
function sleep(n) {
return new Promise((res) => {
setTimeout(() => {
res("hi~");
}, n)
});
};
async function promiseTest() {
const time = Date.now();
const res = await sleep(3000);
console.log(res + "--------------->" + (Date.now() - time));
};
promiseTest();
4.函数被调用无数次,只执行n次
function fn (n) {
let count = 0;
return function () {
if (count < n) {
console.log("[run fn~]");
}
count++;
}
}
const _fn = fn(2);
_fn();
_fn();
_fn();
_fn();
5.手写filter函数
Array.prototype._filter = function (callback, _this) {
const res = [];
for (let i = 0; i < this.length; i++) {
if (callback.call(_this, this[i], i, this)) {
res.push(this[i]);
}
}
return res;
};
const arr = [1, 2, 3, 4, 5];
const _arr = arr._filter((value) => value > 3);
console.log(_arr);
6.求数组的最值和第二大的值
// 找数组中的最大值和第二大的值
// 时间复杂度控制在o(n)
// 不使用 Math.max() Array.sort()
function findArr (arr) {
const len = arr.length;
if (len === 0) return [];
let max = -Infinity, _max = -Infinity;
for (let i = 0; i < len; i++) {
if (i === 0) {
max = arr[0];
_max = arr[0];
} else {
if (arr[i] > max) {
_max = max;
max = arr[i];
}
if (arr[i] < max && arr[i] > _max) {
_max = arr[i];
}
}
}
return [max, _max];
}
console.log(findArr([3, 1, 2, 4, 5, 7, 10, 9]));
console.log(findArr([3, 1, 2, 4, 5, 7, 10, 9]))
7.数组去重的5种方法
let arr = [1, 1, 1, 2, 11, 11, 12, 2, 2, 3, 4, 5];
// 1.通过 set
const resArr1 = [...new Set(arr)];
console.log(resArr1);
// 2.通过 forEach includes
const resArr2 = [];
arr.forEach(value => {
if (!resArr2.includes(value)) {
resArr2.push(value);
}
});
console.log(resArr2);
// 3.通过 forEach indexOf
const resArr3 = [];
arr.forEach(value => {
if (resArr3.indexOf(value) === -1) {
resArr3.push(value);
}
});
console.log(resArr3);
// 4.通过 filter
const resArr4 = arr.filter((value, index, array) => {
return array.indexOf(value) === index;
});
console.log(resArr4);
// 5.通过 reduce
const resArr5 = arr.reduce((ans, value) => {
return ans.includes(value) ? ans : [...ans, value];
}, []);
console.log(resArr5);
8.防抖和节流
防抖简单的来说就是在一系列连续触发的事件中,只触发最后一次事件的响应,适用于input等输入框的优化。
const fangdou = function(fn, delay) {
let timer = null;
return function() {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, arguments);
clearTimeout(timer);
}, delay);
}
};
节流就是在一系列连续触发的事件中,只触发第一次事件的响应,适用于按钮的点击优化。
const jieliu = function(fn, delay) {
let timer = null;
return function() {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, arguments);
timer = null;
clearTimeout(timer);
}, delay);
}
}
};
9.手写call,apply,bind
call,apply,bind的作用是改变所调用函数的this指向,不同之处是call传入的函数是一个一个传入的,apply传入的是一个函数参数的数组,而bind会返回一个函数,参数也是一个一个传入的。
实现的思路也很简单,在所传入的对象中新添加一个属性,这个属性指向所调用的函数,使用这个传入的对象去调用函数,即可隐式的改变this的指向(this指向所调用的对象)。但是在bind函数中需要注意的是返回的函数可能使用new操作符调用,因为new的有的优先级比call,apply,bind高,那么此时应使用new初始化函数。此外,所返回的函数也可以传入参数。
call
Function.prototype.myCall = function(context, ...args) {
context = context || window;
const key = Symbol("key");
context[key] = this;
const result = context[key](...args);
delete context[key];
return result;
};
apply
Function.prototype.myApply = function(context, args) {
context = context || window;
args = args || [];
const key = Symbol("key");
context[key] = this;
const result = context[key](...args);
delete context[key];
return result;
}
bind
Function.prototype.myBind = function(context, ...args) {
context = context || window;
const fn = this;
return function _fn(..._args) {
if (this instanceof _fn) {
return new fn(...args, ..._args); // new 的优先级比bind,call,apply高
}
const key = Symbol("key");
context[key] = fn;
const result = context[key](...args, ..._args);
delete context[key];
return result;
}
};
10.函数柯里化
函数柯里化的定义:将函数进行转化,返回一个新的函数,如果函数接收的参数等于实际参数时就执行该函数。
const text = function(x1, x2, x3) {
console.log(x1 + x2 + x3);
};
// 柯里化函数
function curry(fn, ...args) {
const len = fn.length;
args = args || [];
return function(..._args) {
const params = [...args, ..._args];
if (params.length < len) {
return curry.call(this, fn, ...params);
} else {
return fn.apply(this, params);
}
}
};
const _curry = curry(text);
_curry(1, 2, 3);
_curry(1, 2)(3);
_curry(1)(2)(3);
11.数组扁平化
数组的扁平化就是将一个多维的数组转为一个一维的数组。数组扁平化的方法如下:
- 使用es6新增的flat函数。
- 如果数组中全为数字可以使用arr.toString().split(",").map(item => parseFloat(item))。
- 可以使用Json.stringfy将数组转为字符串,之后使用正则替换[]在进行字符串的拼接。
- 使用递归加reduce:
const arr = [1, [2, [3, [4, 5]]], 6];
const flat = function(arr) {
return arr.reduce((cur, item) => {
return cur.concat(Array.isArray(item) ? flat(item) : item);
}, []);
}
console.log(flat(arr));