如何实现一个简单的 Promise
js 中什么是 softbind,与 bind 有什么区别,如何实现
bind 函数多次调用会已第一次绑定的 this 为准,softbind 已最后一次绑定传入的 this 为准;
Function.prototype.softBind = function(context, ...args) {
const originalFunction = this; // 保存原始函数
return function(...newArgs) {
const currentContext = this instanceof originalFunction ? this : context;
return originalFunction.apply(currentContext, args.concat(newArgs));
};
};
如何实现 promise.map,限制 promise 并发数
首先看 Promise.all 方法,很简单,他会等所有 Promise 执行完毕后,把结果放在数组里,注意这里 Promise 的执行不存在什么顺序还是并发问题,因为 Promise 本身就代表一个已经执行的过程,所以 Promise.all 就是在等所有过程结束,代码:
Promise.all(data.map((v, k) => makePromise(k, v))).then(res => {
console.log(res);
});
如果使用 Promise.map 方法,这个相当于把常见的 Array.map 创建 Promise 的过程和 Promise.all 结合起来,所以用 Promise.map 来实现上例的代码是:
Promise.map(data, (v, k) => makePromise(k, v)).then(res => {
console.log(res)
});
要实现一个 promise.map
函数,限制 Promise 的并发数,可以使用以下步骤:
- 创建一个函数,它接收一个数组和一个异步处理函数。
- 使用一个队列来管理当前正在执行的 Promise。
- 控制并发数,确保一次只执行指定数量的 Promise。
示例代码:
function promiseMap(array, asyncFunc, concurrency) {
const results = new Array(array.length); // 存储结果
let currentIndex = 0; // 当前处理的索引
let activePromises = 0; // 当前活动的 Promise 数量
return new Promise((resolve, reject) => {
function enqueue() {
if (currentIndex === array.length && activePromises === 0) {
// 所有 Promise 完成,返回结果
return resolve(results);
}
while (activePromises < concurrency && currentIndex < array.length) {
const index = currentIndex++; // 获取当前索引并递增
activePromises++; // 增加活动的 Promise 计数
// 调用异步函数并处理结果
asyncFunc(array[index])
.then(result => {
results[index] = result; // 存储结果
})
.catch(reject) // 捕获错误
.finally(() => {
activePromises--; // Promise 完成,减少活动计数
enqueue(); // 继续处理下一个
});
}
}
enqueue(); // 启动初始的 Promise
});
}
// 使用示例
const delay = (ms, value) => new Promise(resolve => setTimeout(() => resolve(value), ms));
const tasks = [1000, 2000, 1500, 3000, 2500].map(ms => () => delay(ms, ms));
promiseMap(tasks.map(task => task()), (task) => task, 2)
.then(results => {
console.log('Results:', results); // 输出结果
})
.catch(err => {
console.error('Error:', err);
});
详细说明
promiseMap
函数:- 接受三个参数:一个数组
array
、一个异步处理函数asyncFunc
,和一个限制并发数的concurrency
。
- 接受三个参数:一个数组
结果数组:
results
用于存储每个 Promise 的结果,按照原数组的顺序。
活动 Promise 计数:
activePromises
用于跟踪当前正在执行的 Promise 数量。
enqueue
函数:- 这个函数负责在当前并发数未达到限制时,启动新的 Promise。
- 它会检查是否所有 Promise 都已完成,并在适当的时候递归调用自己,以继续处理下一个 Promise。
使用示例:
- 创建一个
delay
函数模拟异步操作,并使用promiseMap
来处理这些异步操作,限制并发为 2。
- 创建一个
总结
通过这种方式,你可以实现一个 promise.map
函数,能够限制 Promise 的并发数。这在处理大量异步操作时非常有用,能够有效控制资源使用。如果有其他问题或需要进一步的帮助,请随时问我!
如何实现 compose 函数,进行函数合成
在 JavaScript 中,函数合成(function composition)是将多个函数组合成一个函数,使得每个函数的输出可以作为下一个函数的输入。下面是如何实现一个 compose
函数的示例。
实现 compose
函数
这里提供一个简单的实现,同时支持多个函数的组合:
function compose(...fns) {
return function (initialValue) {
return fns.reduceRight((acc, fn) => fn(acc), initialValue);
};
}
// 使用示例
const add = x => x + 1;
const multiply = x => x * 2;
const subtract = x => x - 3;
// 组合函数
const composedFunction = compose(subtract, multiply, add);
// 调用组合函数
const result = composedFunction(5); // ((5 + 1) * 2) - 3
console.log(result); // 输出: 9
详细说明
compose
函数:- 接收一个或多个函数作为参数(
...fns
),并返回一个新的函数。 - 新函数接受一个初始值
initialValue
。
- 接收一个或多个函数作为参数(
reduceRight
:- 使用
reduceRight
方法从右到左应用函数组合,确保最后一个函数的输出作为倒数第二个函数的输入,依此类推。
- 使用
函数应用:
- 在
reduceRight
中,acc
是累积的值,fn
是当前处理的函数。每次调用fn(acc)
,将acc
更新为当前函数的输出。
- 在
使用示例
- 定义了一些简单的函数:
add
、multiply
和subtract
。 - 使用
compose
将这些函数组合起来。 - 调用组合函数
composedFunction(5)
,计算过程如下:5 + 1 = 6
6 * 2 = 12
12 - 3 = 9
总结
通过实现 compose
函数,可以轻松地将多个函数组合在一起,从而增强代码的可重用性和可读性。你可以根据需要扩展这个实现,例如支持异步函数或处理错误。如果有其他问题或需要进一步的帮助,请随时问我!
如何实现类似 lodash.get 函数
function get(obj, path, defaultValue) {
// 将 path 转换为数组
const keys = Array.isArray(path) ? path : path.split('.');
// 使用 reduce 遍历嵌套属性
const result = keys.reduce((acc, key) => {
// 如果 acc 为 undefined,返回 undefined
if (acc === undefined || acc === null) {
return undefined;
}
return acc[key]; // 访问嵌套属性
}, obj);
// 如果 result 为 undefined,则返回 defaultValue
return result === undefined ? defaultValue : result;
}
// 使用示例
const data = {
user: {
name: 'Alice',
address: {
city: 'Wonderland',
zip: 12345
}
}
};
console.log(get(data, 'user.name')); // 输出: Alice
console.log(get(data, 'user.address.city')); // 输出: Wonderland
console.log(get(data, 'user.age', 'N/A')); // 输出: N/A
console.log(get(data, 'user.address.zip')); // 输出: 12345
console.log(get(data, 'user.address.country', 'Unknown')); // 输出: Unknown
如何实现一个 flatMap 函数 (头条)
function flatMap(arr, fn) {
return arr.reduce((acc, cur) => {
return acc.concat(fn(cur));
}, []);
}
// 使用示例
const arr = [1, 2, 3];
const fn = x => [x, x * 2];
console.log(flatMap(arr, fn)); // 输出: [1, 2, 2, 4, 3, 6]
如何实现一个 async/await
/**
* async的执行原理
* 其实就是自动执行generator函数
* 暂时不考虑genertor的编译步骤(更复杂)
*/
const getData = () =>
new Promise((resolve) => setTimeout(() => resolve("data"), 1000));
// 这样的一个async函数 应该再1秒后打印data
async function test() {
const data = await getData();
console.log("data: ", data);
const data2 = await getData();
console.log("data2: ", data2);
return "success";
}
// async函数会被编译成generator函数 (babel会编译成更本质的形态,这里我们直接用generator)
function* testG() {
// await被编译成了yield
const data = yield getData();
console.log("data: ", data);
const data2 = yield getData();
console.log("data2: ", data2);
return "success";
}
function asyncToGenerator(generatorFunc) {
return function () {
const gen = generatorFunc.apply(this, arguments);
return new Promise((resolve, reject) => {
function step(key, arg) {
let generatorResult;
try {
generatorResult = gen[key](arg);
} catch (error) {
return reject(error);
}
const { value, done } = generatorResult;
if (done) {
return resolve(value);
} else {
return Promise.resolve(value).then(
function onResolve(val) {
step("next", val);
},
function onReject(err) {
step("throw", err);
},
);
}
}
step("next");
});
};
}
const testGAsync = asyncToGenerator(testG);
testGAsync().then((result) => {
console.log(result);
});
如何使用 async/await 实现 Promise.all 的效果
const all = (list) => {
const res = new Promise((resolve, reject) => {
let length = list && list.length
let count = 0
let result = []
if(!list || list.length === 0) {
resolve(result)
}
list.forEach(async (item, index) => {
try {
const res = await item
result[index] = res
count ++
if(count === length) {
resolve(result)
}
} catch(err) {
reject(err)
}
});
})
return res
}
使用 js 实现一个 lru cache
class Node {
constructor(key, value) {
this.key = key;
this.value = value;
this.prev = null;
this.next = null;
}
}
class LRUCache {
constructor(capacity) {
this.capacity = capacity; // 缓存容量
this.cache = new Map(); // 哈希表存储缓存
this.head = new Node(null, null); // 虚拟头节点
this.tail = new Node(null, null); // 虚拟尾节点
this.head.next = this.tail; // 连接头尾
this.tail.prev = this.head;
}
// 将节点移动到链表的头部
_moveToHead(node) {
node.prev.next = node.next;
node.next.prev = node.prev;
node.prev = this.head;
node.next = this.head.next;
this.head.next.prev = node;
this.head.next = node;
}
// 删除链表的尾部节点
_removeTail() {
const node = this.tail.prev;
this.tail.prev = node.prev;
node.prev.next = this.tail;
this.cache.delete(node.key);
}
// 获取缓存
get(key) {
if (!this.cache.has(key)) {
return -1; // 如果不存在,返回 -1
}
const node = this.cache.get(key);
this._moveToHead(node); // 移动到头部
return node.value; // 返回值
}
// 设置缓存
put(key, value) {
if (this.cache.has(key)) {
const node = this.cache.get(key);
node.value = value; // 更新值
this._moveToHead(node); // 移动到头部
} else {
const newNode = new Node(key, value);
this.cache.set(key, newNode); // 添加到哈希表
this._moveToHead(newNode); // 移动到头部
if (this.cache.size > this.capacity) {
this._removeTail(); // 超过容量时移除尾部
}
}
}
}
// 使用示例
const lruCache = new LRUCache(2);
lruCache.put(1, 1); // 缓存是 {1=1}
lruCache.put(2, 2); // 缓存是 {1=1, 2=2}
console.log(lruCache.get(1)); // 返回 1
lruCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
console.log(lruCache.get(2)); // 返回 -1 (未找到)
lruCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
console.log(lruCache.get(1)); // 返回 -1 (未找到)
console.log(lruCache.get(3)); // 返回 3
console.log(lruCache.get(4)); // 返回 4
实现一个 once 函数,记忆返回结果只执行一次
function once(fn) {
let executed = false; // 标志位,表示函数是否已执行
let result; // 存储返回结果
return function(...args) {
if (!executed) {
executed = true; // 标记为已执行
result = fn(...args); // 执行并存储结果
}
return result; // 返回结果
};
}
// 使用示例
const add = (a, b) => a + b;
const onceAdd = once(add);
console.log(onceAdd(1, 2)); // 输出: 3
console.log(onceAdd(4, 5)); // 仍然输出: 3 (函数未再执行)
如何实现一个无限累加的 sum 函数
https://q.shanyue.tech/fe/js/428
//更多描述 实现一个 sum 函数如下所示:
sum(1, 2, 3).valueOf(); //6
sum(2, 3)(2).valueOf(); //7
sum(1)(2)(3)(4).valueOf(); //10
sum(2)(4, 1)(2).valueOf(); //9
sum(1)(2)(3)(4)(5)(6).valueOf(); // 21
function sum(initialValue = 0) {
let total = initialValue; // 初始化总值
// 定义一个累加函数
function add(value) {
total += value; // 累加传入的值
return add; // 返回自身,以便链式调用
}
// 添加一个方法来获取总值
add.valueOf = function() {
return total; // 返回当前总值
};
return add; // 返回累加函数
}
// 使用示例
const result = sum(5)(10)(15); // 5 + 10 + 15
console.log(result.valueOf()); // 输出: 30
console.log(sum()(1)(2)(3).valueOf()); // 输出: 6
JS 如何实现一个同步的 sleep 函数
function sleep(milliseconds) {
const start = Date.now();
while (Date.now() - start < milliseconds) {
// 空循环,阻塞线程
}
}
// 使用示例
console.log('Start');
sleep(2000); // 暂停 2 秒
console.log('End');
JS 如何实现一个 sleep/delay 函数
const sleep = (seconds) =>
new Promise((resolve) => setTimeout(resolve, seconds));
function delay(func, seconds, ...args) {
return new Promise((resolve, reject) => {
setTimeout(() => {
Promise.resolve(func(...args))
.then(resolve)
.catch(reject);
}, seconds);
});
}
https://q.shanyue.tech/fe/js/442
如何实现一个 sample 函数,从数组中随机取一个元素
function sample(array) {
if (array.length === 0) {
return undefined; // 如果数组为空,返回 undefined
}
const randomIndex = Math.floor(Math.random() * array.length); // 生成随机索引
return array[randomIndex]; // 返回随机元素
}
// 使用示例
const numbers = [1, 2, 3, 4, 5];
const randomElement = sample(numbers);
console.log(randomElement); // 输出: 数组中的随机元素
如何实现一个函数 lodash.merge
function merge(target, ...sources) {
for (const source of sources) {
for (const key in source) {
// 确保源对象的属性是对象且目标对象的对应属性也是对象
if (isObject(source[key]) && isObject(target[key])) {
// 递归合并
target[key] = merge(target[key], source[key]);
} else {
// 否则直接赋值
target[key] = source[key];
}
}
}
return target;
}
// 辅助函数:判断一个值是否是对象
function isObject(value) {
return value !== null && typeof value === 'object';
}
// 使用示例
const obj1 = { a: 1, b: { x: 1 } };
const obj2 = { b: { y: 2 }, c: 3 };
const obj3 = { a: 2, b: { x: 3 } };
const result = merge({}, obj1, obj2, obj3);
console.log(result);
// 输出: { a: 2, b: { x: 3, y: 2 }, c: 3 }
实现一个 inherits 函数进行继承
function inherits(Child, Parent) {
// 设置 Child 的原型为 Parent 的实例
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child; // 确保 constructor 指向 Child
}
// 使用示例
function Parent(name) {
this.name = name;
}
Parent.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
};
function Child(name, age) {
Parent.call(this, name); // 调用 Parent 构造函数
this.age = age;
}
// 使用 inherits 实现继承
inherits(Child, Parent);
// 添加 Child 的方法
Child.prototype.sayAge = function() {
console.log(`I am ${this.age} years old`);
};
// 测试
const childInstance = new Child('Alice', 10);
childInstance.sayHello(); // 输出: Hello, my name is Alice
childInstance.sayAge(); // 输出: I am 10 years old
如何实现一个深比较的函数 deepEqual
function deepEqual(obj1, obj2) {
// 如果两个值是相同的引用,直接返回 true
if (obj1 === obj2) return true;
// 如果任一值为 null 或不是对象,返回 false
if (obj1 == null || obj2 == null || typeof obj1 !== 'object' || typeof obj2 !== 'object') {
return false;
}
// 获取对象的所有键
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
// 比较键的数量
if (keys1.length !== keys2.length) return false;
// 递归比较每个键的值
for (const key of keys1) {
if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
return false;
}
}
return true; // 如果所有键都匹配,返回 true
}
// 使用示例
const objA = { a: 1, b: { c: 2 } };
const objB = { a: 1, b: { c: 2 } };
const objC = { a: 1, b: { c: 3 } };
console.log(deepEqual(objA, objB)); // 输出: true
console.log(deepEqual(objA, objC)); // 输出: false
console.log(deepEqual(objA, null)); // 输出: false
console.log(deepEqual(objA, objA)); // 输出: true
实现二进制与十进制的互相转化的两个函数
// 十进制转二进制
function decimalToBinary(decimal) {
if (decimal < 0) {
throw new Error("输入必须是非负整数");
}
return decimal.toString(2); // 使用 toString(2) 转换为二进制
}
// 二进制转十进制
function binaryToDecimal(binary) {
if (!/^[01]+$/.test(binary)) {
throw new Error("输入必须是有效的二进制字符串");
}
return parseInt(binary, 2); // 使用 parseInt 转换为十进制
}
// 使用示例
const decimal = 10;
const binary = decimalToBinary(decimal);
console.log(`十进制 ${decimal} 转换为二进制: ${binary}`); // 输出: 10 转换为 1010
const binaryInput = "1010";
const decimalOutput = binaryToDecimal(binaryInput);
console.log(`二进制 ${binaryInput} 转换为十进制: ${decimalOutput}`); // 输出: 1010 转换为 10
如何取得一个数字的小数部分与整数部分
function getIntegerAndDecimalParts(number) {
const integerPart = Math.floor(number); // 获取整数部分
const decimalPart = number - integerPart; // 计算小数部分
return {
integer: integerPart,
decimal: decimalPart
};
}
// 使用示例
const number = 5.75;
const parts = getIntegerAndDecimalParts(number);
console.log(`整数部分: ${parts.integer}`); // 输出: 整数部分: 5
console.log(`小数部分: ${parts.decimal}`); // 输出: 小数部分: 0.75
实现 batchFn 函数,可以批量执行函数
function batchFn(functions, ...args) {
// 使用 map 遍历函数数组并调用每个函数
return functions.map(fn => fn(...args));
}
// 使用示例
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const subtract = (a, b) => a - b;
const functions = [add, multiply, subtract];
const results = batchFn(functions, 5, 3);
console.log(results); // 输出: [8, 15, 2]