如何实现一个简单的 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]