深拷贝
function deepClone(obj, cache = new WeakMap()) {
// 检查是否已经克隆过该对象,避免循环引用
if (cache.has(obj)) {
return cache.get(obj);
}
if (obj === null || typeof obj !== "object" || typeof value === "function") {
return obj;
}
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
const result = Array.isArray(obj)
? []
: Object.create(Object.getPrototypeOf(obj));
cache.set(obj, result);
for (const key of Object.keys(obj)) {
const value = obj[key];
result[key] = deepClone(value, cache);
}
return result;
}
计算嵌套对象里各个 value 值的总和
function sum(obj) {
let total = 0
function f(value) {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
f(value[key])
}
} else {
total += value
}
}
f(obj)
return total
}
数组去重
// 1.indexOf 或 includes
function unique(arr) {
let res = []
for (const ele of arr) {
if (res.indexOf(ele) === -1) res.push(ele)
// if (!res.includes(ele)) res.push(ele)
}
return res
}
// 2.Set
function unique(arr) {
return [...new Set(arr)]
}
// 3.filter
function unique(arr) {
return arr.filter((ele, index, arr) => arr.indexOf(ele) === index)
}
数组扁平化
// 1.递归
function flat(arr, depth = 1) {
let res = []
for (const ele of arr) {
if (Array.isArray(ele) && depth > 0) {
res = res.concat(flat(ele, depth - 1))
} else {
res.push(ele)
}
}
return res
}
// 2.reduce
function flat(arr, depth = 1) {
return depth > 0
? arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) ? flat(cur, depth - 1) : cur), [])
: arr
}
实现 call/apply
const obj = {a: 1}
const func = function (...args) {
console.log(this, args)
}
// 本质就是要构造一个这样的对象:
// obj = {
// a: 1,
// Symbol(): func
// }
Function.prototype.call = function (context) {
context = Object(context) // 原始值变成包装类型
const fnSymbol = Symbol() // 避免属性冲突
context[fnSymbol] = this // this 此时是调用 call 的原函数
const args = [...arguments].slice(1) // 获取调用 call 时传入的参数
const res = context[fnSymbol](...args) // 执行对象下的原函数,此时原函数的 this 执行的就是这个对象
delete context[fnSymbol]
return res
}
// apply 同上,只是参数有点区别
Function.prototype.apply = function (context, arr = []) {
context = Object(context) // 原始值变成包装类型
const fnSymbol = Symbol() // 避免属性冲突
context[fnSymbol] = this // this 此时是调用 call 的原函数
const res = context[fnSymbol](...arr) // 执行对象下的原函数,此时原函数的 this 执行的就是这个对象
delete context[fnSymbol]
return res
}
实现 bind
Function.prototype.bind = function (context) {
const fn = this // this 就是调用 bind 的源函数
const args = [...arguments].slice(1) // 获取调用 bind 时传入的参数
return function(...moreArgs) {
return fn.apply(context, args.concat(...moreArgs))
}
}
Promise.all
全部成功则成功,有一个失败则失败
function promiseAll(promises) {
// 判断输入是否为 Iterable
const isIterable = typeof promises[Symbol.iterator] === "function"
// 不是的话就回传错误讯息
if (!isIterable) {
return new TypeError("Arguments must be iterable")
}
// 把 Iterable 转成 Array
promises = Array.from(promises)
const outputs = []; // 先定义一个最终要 resolve 的 outputs,之后每个 promise 被 fulfilled 时,就放到 outputs 里面
let resolveCounter = 0; // 记录有多少个 promise 已经 fulfilled
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((value) => {
outputs[index] = value
resolveCounter += 1
if (resolveCounter === promises.length) {
resolve(outputs)
}
})
.catch((err) => {
reject(err)
})
})
})
}
Promise.any
取第一个成功的
function promiseAny(promises) {
// 省略参数校验...
const errors = [] // 存储所有 Promise 的拒绝原因
let rejectedCount = 0 // 记录已拒绝的 Promise 数量
return new Promise((resolve, reject) => {
// 迭代过 promises
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((value) => {
// 只要有 fulfill 的,就马上 resolve
resolve(val)
})
.catch((err) => {
errors[index] = err // 记录 err 信息
rejectedCount++
if (rejectedCount === promises.length) reject(new AggregateError(errors, 'All promises were rejected')) // 全部 rejected 则返回错误
})
})
})
}
Promise.race
取第一个完成的
function promiseRace(promises) {
// 省略参数校验...
return new Promise((resolve, reject) => {
// 迭代过 promises
for (const p of promises) {
p.then((val) => {
// 只要有 fulfill 的,就马上 resolve
resolve(val)
}).catch((e) => {
// 或是只要有 reject 的,就马上 reject
reject(e)
})
}
})
}
Promise.allSettled
全部完成后返回对应的结果
function promiseAllSettled(promises) {
// 省略参数校验...
const outputs = [] // 记录结果
let settledCount = 0 // 记录已完成的 Promise 数量
return new Promise((resolve, reject) => {
// 迭代过 promises
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((value) => {
// fulfill 的放入结果中
outputs[index] = { status: 'fulfilled', value: value }
settledCount++
if (settledCount === promises.length) resolve(outputs) // 全部完成了,返回结果
})
.catch((err) => {
// rejected 的放入结果中
outputs[index] = { status: 'rejected', reason: err }
settledCount++
if (settledCount === promises.length) resolve(outputs) // 全部完成了,返回结果
})
})
})
}
Promise 限制并发数量
请实现 PLimit 类,该类可以控制异步函数的并发数量
const limit = new PLimit(2); // 设置并发数为 2
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const task = async () => {
console.log('Start task');
await delay(1000); // 模拟一个耗时的任务
console.log('End task');
};
Promise.all([
limit.run(task),
limit.run(task),
limit.run(task),
limit.run(task),
limit.run(task),
limit.run(task),
limit.run(task),
limit.run(task),
limit.run(task),
limit.run(task),
]).then(() => console.log("done"));
// 请补充下面的代码片段
class PLimit {
constructor(limit) {
this.limit = limit;
// 请补充
}
run(fn) {
// 请补充
}
}
答案如下:
class PLimit {
constructor(limit) {
this.limit = limit;
this.queue = []; // 任务队列
this.running = 0; // 当前运行任务数
}
run(fn) {
return new Promise((resolve, reject) => {
this.queue.push({ fn, resolve, reject }); // 每个任务要跟它自身的 resolve 和 reject 相对应
this.execute();
});
}
execute() {
// 当前运行任务数量超过限制 || 任务队列为空
if (this.running >= this.limit || !this.queue.length) return;
// 开始一个新任务
this.running++;
let { fn, resolve, reject } = this.queue.shift();
// 执行它
fn()
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
})
.finally(() => {
// 任务结束,进行下一个任务
this.running--;
this.execute();
// 如果要每次执行完 limit 个再执行下 limit 个,则把上面的 this.execute() 替换成:
// if (this.running === 0) {
// for (let i = 0; i < this.limit; i++) {
// this.execute();
// }
// }
});
}
}
实现一个 LazyMan,可以按照以下方式调用:
LazyMan("Hank")
输出:
Hi! This is Hank!
LazyMan("Hank").sleep(10).eat("dinner")
输出:
Hi! This is Hank!
//等待10秒..
Wake up after 10
Eat dinner
LazyMan("Hank").eat("dinner").eat("supper")
输出:
Hi! This is Hank!
Eat dinner
Eat supper
LazyMan("Hank").sleepFirst(5).eat("supper")
输出:
//等待5秒
Wake up after 5
Hi! This is Hank!
Eat supper
function LazyMan(name) {
const queue = [] // 任务队列
// 执行函数,依次取任务队列中的任务进行执行
function run() {
if (!queue.length) return
const task = queue.shift()
task()
}
// 保证在执行完同步任务后再执行 run
setTimeout(() => {
run()
})
// 函数调用时的初始化任务
const sayHi = () => {
console.log(`Hi! This is ${name}!`)
run()
}
queue.push(sayHi)
// 要实现链式调用,需要定义一个操作映射对象,并返回这个 obj
const obj = {
sleep: (time) => {
queue.push(() => {
setTimeout(() => {
console.log(`Wake up after ${time}`)
run()
}, time * 1000)
})
return obj
},
eat: (something) => {
queue.push(() => {
console.log(`Eat ${something}`)
run()
})
return obj
},
sleepFirst: (time) => {
queue.unshift(() => {
setTimeout(() => {
console.log(`Wake up after ${time}`)
run()
}, time * 1000)
})
return obj
},
}
return obj
}