一、控制并发数
//实现方式1:函数
function createLimitPromise(limitNum, promiseListRaw) {
let resArr = [];
let handling = 0;
let resolvedNum = 0;
let promiseList = [...promiseListRaw];
let runTime = promiseListRaw.length;
return new Promise((resolve) => {
//并发执行limitNum 次
for (let i = 1; i <= limitNum; i++) {
run();
}
function run() {
if (!promiseList.length) return;
handling += 1;
handle(promiseList.shift())
.then((res) => {
resArr.push(res);
})
.catch((e) => {
//ignore
console.log("catch error");
})
.finally(() => {
handling -= 1;
resolvedNum += 1;
if (resolvedNum === runTime) {
resolve(resArr);
}
run();
});
}
function handle(requestFn) {
return new Promise((resolve, reject) => {
requestFn()
.then((res) => resolve(res))
.catch((e) => reject(e));
});
}
});
}
//实现方式二:类
class Scheduler {
constructor(limit) {
this.queue = [];
this.maxCounter = limit;
this.runCounter = 0;
}
add(time, val) {
const promiseEvent = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(val);
resolve();
}, time);
});
};
this.queue.push(promiseEvent);
}
start() {
for (let i = 0; i < this.maxCounter; i++) {
this.request();
}
}
request() {
if (
!this.queue ||
!this.queue.length ||
this.runCounter >= this.maxCounter
) {
return;
}
this.runCounter++;
this.queue
.shift()()
.then((res) => {
this.runCounter--;
this.request();
});
}
}
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
scheduler.add(time, order);
};
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
scheduler.start();
//实现方式三
function timeout(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
}
class SuperTask {
constructor(MAX_NUM = 2) {
this.tasks = [];
this.count = 0;
this.max_num = MAX_NUM;
}
add(task) {
return new Promise((resolve, reject) => {
this.tasks.push({ task, resolve, reject });
this.run();
});
}
run() {
while (this.count < this.max_num && this.tasks.length > 0) {
this.count++;
const { task, resolve, reject } = this.tasks.shift();
task()
.then(resolve, reject)
.finally(() => {
this.count--;
this.run();
});
}
}
}
const superTask = new SuperTask();
function addTask(time, name) {
superTask
.add(() => timeout(time))
.then(() => {
console.log(`任务${name}完成`);
});
}
addTask(10000, 1);
addTask(5000, 2);
addTask(3000, 3);
addTask(4000, 4);
addTask(5000, 5);
面试失败总结:createLimitPromise没有返回值,无法读取到resArr
二、发布订阅模式
class EventEmitter {
constructor() {
this.event = {};
}
on(type, cb) {
if (this.event[type]) {
this.event[type].push(cb);
} else {
this.event[type] = [cb];
}
}
once(type, cb) {
const fn = (...args) => {
cb(...args);
this.off(type, fn);
};
this.on(type, fn);
}
emit(type, ...args) {
if (this.event[type]) {
this.event[type].forEach((cb) => cb(...args));
} else {
return;
}
}
off(type, cb) {
if (this.event[type]) {
this.event[type] = this.event[type].filter((item) => item != cb);
} else {
return;
}
}
}
// 运行示例
let ev = new EventEmitter();
const fun1 = (str) => {
console.log(str);
};
ev.on("say", fun1);
ev.emit("say", "visa");
// ev.off("say", fun1);
ev.once("say1", fun1);
ev.emit("say1", "visa1");
ev.emit("say1", "visa1");
ev.emit("say", "visa");
总结:面试的时候没写出来。复盘后发现思路很简单。写一个类,勾造函数里定义一个事件对象(事件类型是键值,事值是事件数组)。on注册事件,就是在事件对象里添加事件,emit是把事件类型的每个事件都执行一遍。off是删除事件类型的某个事件,用数组filter,once是把事件用一个函数包裹住,并且在函数里调用off。
三、数组排平加排序和去重
const arrFlat = (arr) => {
const res = [];
const fn = (arr) => {
for (let i = 0; i < arr.length; i++)
if (Array.isArray(arr[i])) {
fn(arr[i]);
} else {
res.push(arr[i]);
}
};
//数组拍平
fn(arr);
//数组去重加升序
const setData = Array.from(new Set(res)).sort((a, b) => a - b);
return setData;
};
const arr = [1, 2, [3, 6, 1, 3], [7.5, 9, [1, 2, 3]]];
console.log(arrFlat(arr));
四、字符串类型转数
const str = `Language
JavaScript
TypeScript
NodeJS
HTML
Server
DataBase
MongoDB
System
Linux
Window`;
const strSplit = str.split("\n");
const strArr = [];
const spaceNum = [];
const json = {};
strArr.push(json);
spaceNum.push(-1);
for (let i = 0; i < strSplit.length; i++) {
const num = strSplit[i].lastIndexOf(" ") + 1;
const str = strSplit[i].replace(/\s/g, "");
const curOcj = { val: str };
while (num <= spaceNum.slice(-1)) {
strArr.pop();
spaceNum.pop();
}
strArr.slice(-1)[0].children
? strArr.slice(-1)[0].children.push(curOcj)
: (strArr.slice(-1)[0].children = [curOcj]);
strArr.push(curOcj);
spaceNum.push(num);
}
console.log(json);
五、数组转树和树转数组
const list = [
{ id: 12, parent_id: 1, name: "朝阳区" },
{ id: 241, parent_id: 24, name: "田林街道" },
{ id: 31, parent_id: 3, name: "广州市" },
{ id: 13, parent_id: 1, name: "昌平区" },
{ id: 2421, parent_id: 242, name: "上海科技绿洲" },
{ id: 21, parent_id: 2, name: "静安区" },
{ id: 242, parent_id: 24, name: "漕河泾街道" },
{ id: 22, parent_id: 2, name: "黄浦区" },
{ id: 11, parent_id: 1, name: "顺义区" },
{ id: 2, parent_id: 0, name: "上海市" },
{ id: 24, parent_id: 2, name: "徐汇区" },
{ id: 1, parent_id: 0, name: "北京市" },
{ id: 2422, parent_id: 242, name: "漕河泾开发区" },
{ id: 32, parent_id: 3, name: "深圳市" },
{ id: 33, parent_id: 3, name: "东莞市" },
{ id: 3, parent_id: 0, name: "广东省" },
];
//数组转树
const arrToTree = (list) => {
const res = [];
const map = {};
for (let item of list) {
map[item.id] = item;
}
for (let obj of list) {
if (map[obj.parent_id]) {
(
map[obj.parent_id].children || (map[obj.parent_id].children = [])
).push(obj);
} else {
res.push(obj);
}
}
return res;
};
const tree = arrToTree(list);
console.log(tree);
//树转数组
const treeToArr = (tree) => {
const copyTree = [].concat(tree);
const res = [];
while (true) {
const item = copyTree.shift();
if (!item) return res;
if (item.children) {
copyTree.push(...item.children);
delete item.child;
}
res.push(item);
}
};
console.log(treeToArr(tree));
六、顺时针螺旋打印二维数组
const rows = matrix.length,
cols = matrix[0].length;
let top = 0,
left = 0,
bottom = rows - 1,
right = cols - 1;
const res = [];
while (left <= right && top <= bottom) {
for (let col = left; col <= right; col++) {
res.push(matrix[top][col]);
}
top++;
for (let row = top; row <= bottom; row++) {
res.push(matrix[row][right]);
}
right--;
if (top <= bottom && left <= right) {
for (let col = right; col >= left; col--) {
res.push(matrix[bottom][col]);
}
bottom--;
for (let row = bottom; row >= top; row--) {
res.push(matrix[row][left]);
}
left++;
}
}
return res;
};
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
const result = fn(matrix);
console.log(result); // 输出: [1, 2, 3, 6, 9, 8, 7, 4, 5]
七、单例模式
//1 构造函数写法
let singeDemo = null;
function SingleDemo1() {
if (!singeDemo) {
singeDemo = this;
}
return singeDemo;
}
SingleDemo1.prototype.show = function () {
console.log("我是一个实例");
};
const a = new SingleDemo1();
const b = new SingleDemo1();
a.show();
console.log(a === b);
//2 静态方法写法
class SingleDemo2 {
show() {
console.log("我是一个实例");
}
static getInstance() {
if (!SingleDemo2.instance) {
SingleDemo2.instance = new SingleDemo2();
}
return SingleDemo2.instance;
}
}
const a2 = SingleDemo2.getInstance();
const b2 = SingleDemo2.getInstance();
a2.show();
console.log(a2 === b2);
//1 闭包写法
class SingleDemo3 {
show() {
console.log("我是一个实例");
}
}
SingleDemo3.getInstance = (function () {
let instance = null;
return function () {
if (!instance) {
instance = new SingleDemo3();
}
return instance;
};
})();
const a3 = SingleDemo3.getInstance();
const b3 = SingleDemo3.getInstance();
a3.show();
console.log(a3 === b3);
八、二维数组去重
const arr = [
[1, 2],
[2, 1],
[3, 4],
[4, 6],
];
const obj = {};
const res = [];
for (let i = 0; i < arr.length; i++) {
const key = arr[i].sort((a, b) => a - b).join(",");
if (!obj[key]) {
res.push(arr[i]);
obj[key] = true;
}
}
console.log(res);
九、对象数组去重
const arr = [
{ a: 1, b: 2 },
{ b: 2, a: 1 },
{ a: 1, b: 2, c: { a: 1, b: 2 } },
{ b: 2, a: 1, c: { b: 2, a: 1 } },
];
const res = [...arr];
const isObject = (val) => typeof val == "object" && typeof val != null;
function isEqual(val1, val2) {
if (!isObject(val1) || !isObject(val2)) return Object.is(val1, val2);
const key1 = Object.keys(val1);
const key2 = Object.keys(val2);
if (key1.length != key2.length) return false;
for (let key of key1) {
if (!val2[key]) return false;
const res = isEqual(val1[key], val2[key]);
if (!res) return false;
}
return true;
}
for (let i = 0; i < res.length; i++) {
for (let j = i + 1; j < res.length; j++) {
if (isEqual(res[i], res[j])) {
res.splice(j, 1);
j--;
}
}
}
console.log(res);