其他手写
RoleTang 9/12/2025
深拷贝
// 不同的类型处理
// 循环引用处理 Map
const cloneDeep = (target, map = new Map()) => {
// 只处理复杂类型,基础类型直接返回
if (typeof target !== 'object' || !target) return target
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
// 已处理
if (map.has(target)) return map.get(target)
let res
if (Object.prototype.toString.call(target) === '[object Map]') {
res = new Map()
map.set(target, res)
target.forEach((item, key) => res.set(key, cloneDeep(item, map)))
} else if (Object.prototype.toString.call(target) === '[object Set]') {
res = new Set()
map.set(target, res)
target.forEach((item) => res.add(cloneDeep(item, map)))
} else {
if (Array.isArray(target)) res = []
else res = {}
map.set(target, res)
for (const key in target) {
if (Object.hasOwnProperty.call(target, key)) {
res[key] = cloneDeep(target[key], map)
}
}
}
return res
}
dom 转 json
function convertToJson() {
const root = document.getElementsByClassName('root')[0];
const output = new Object();
// 只需要这两个标签即可
output.tagName = root.tagName;
output.className = root.className;
output.childs = getChilds(root);
// 此方法非常快速
console.log(JSON.stringify(output));
}
function getChilds(node) {
const childs = node.children;
const result = new Array();
if(!childs || childs.length === 0) return result;
for (const child of childs) {
const childOutput = new Object();
childOutput.tagName = child.tagName;
childOutput.className = child.className;
childOutput.childs = getChilds(child);
result.push(childOutput);
}
return result;
}
convertToJson();
// 展开数组
// 会跳过稀疏数组空槽
const flatArray = (array, depth = 1) => {
if (!Array.isArray(array) || depth === 0) return array
return array.reduce((pre, cur) => {
return pre.concat(flatArray(cur, depth - 1))
}, [])
}
// \s space \d digital \w word
// +量词,1个或者多个 {1,}
// g范围
const trim = (s) => {
return s.replace(/^\s+|\s+$/g, '')
}
// 深比较
const deepEqual = (a, b, visited = new WeakMap()) => {
// 同一引用
if (a === b) return true
// 处理 NaN
if (Number.isNaN(a) && Number.isNaN(b)) return true
// 类型不同
if (typeof a !== typeof b) return false
// null 判断
if (a === null || b === null) return a === b
// 基础类型 (string, number, boolean, symbol, bigint)
if (typeof a !== 'object' && typeof a !== 'function') return a === b
// 循环引用检查
if (visited.has(a)) return visited.get(a) === b
visited.set(a, b)
// Date
if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime()
// RegExp
if (a instanceof RegExp && b instanceof RegExp)
return a.source === b.source && a.flags === b.flags
// Map
if (a instanceof Map && b instanceof Map) {
if (a.size !== b.size) return false
for (const [key, val] of a) {
if (!b.has(key)) return false
if (!deepEqual(val, b.get(key), visited)) return false
}
return true
}
// Set
if (a instanceof Set && b instanceof Set) {
if (a.size !== b.size) return false
// 转换成数组逐个比较(顺序不保证)
for (const val of a) {
// 这里不能简单 b.has(val),要深度比较
let found = false
for (const valB of b) {
if (deepEqual(val, valB, visited)) {
found = true
break
}
}
if (!found) return false
}
return true
}
// Array
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) return false
for (let i = 0; i < a.length; i++) {
if (!deepEqual(a[i], b[i], visited)) return false
}
return true
}
// 普通对象
const keysA = Reflect.ownKeys(a)
const keysB = Reflect.ownKeys(b)
if (keysA.length !== keysB.length) return false
for (const key of keysA) {
if (!keysB.includes(key)) return false
if (!deepEqual(a[key], b[key], visited)) return false
}
return true
}
const compose = (...fns) => {
return fns.reduce(
(pre, cur) =>
(...args) =>
pre(cur(...args))
)
}
// 实现并发数
Promise.map = (promises, concurrent) => {
if (concurrent >= promises.length) return Promise.allSettled(promises)
return new Promise((resolve) => {
const results = new Array(promises.length)
let remaining = promises.length
let nextIndex = 0
const run = (index) => {
const p = promises[index]
if (!p) return
p.then(
(value) => (results[index] = { status: 'fulfilled', value }),
(reason) => (results[index] = { status: 'rejected', reason })
).finally(() => {
remaining--
if (remaining === 0) {
resolve(results)
} else if (nextIndex < promises.length) {
run(nextIndex++)
}
})
}
// 启动前 concurrent 个任务
for (; nextIndex < concurrent; nextIndex++) {
run(nextIndex)
}
})
}
/**
* 参数
* object (Object): 要检索的对象。
* path (Array|string): 要获取属性的路径。
* [defaultValue] (*): 如果解析值是 undefined ,这值会被返回。
* @returns 根据 object对象的path路径获取值。 如果解析 value 是 undefined 会以 defaultValue 取代。
*/
/**
* 参数
* object (Object): 要检索的对象。
* path (Array|string): 要获取属性的路径。
* [defaultValue] (*): 如果解析值是 undefined ,这值会被返回。
* @returns 根据 object对象的path路径获取值。 如果解析 value 是 undefined 会以 defaultValue 取代。
*/
const get = (context, path, defaultValue = undefined) => {
// 把字符串路径解析成数组路径
const parsePath = (path) => {
if (Array.isArray(path)) return path
const pathArr = []
let pathStr = ''
const pushPathStr = () => {
if (pathStr !== '') pathArr.push(pathStr)
pathStr = ''
}
for (let i = 0; i < path.length; i++) {
const char = path[i]
if (char === '.' || char === '[' || char === ']') pushPathStr()
else pathStr += char
}
pushPathStr()
return pathArr
}
// 统一为数组形式
const pathArr = parsePath(path)
// 遍历路径
let res = context
for (let i = 0; i < pathArr.length; i++) {
if (res == null || !(pathArr[i] in res)) {
return defaultValue
}
res = res[pathArr[i]]
}
// 区分 undefined 和 路径不存在
return res === undefined ? defaultValue : res
}
var object = { a: [{ b: { c: 3 } }] }
console.log(get(object, 'a[0].b.c'))
console.log(get(object, 'a[0].b.c'))
// => 3
console.log(get(object, ['a', '0', 'b', 'c']))
// => 3
console.log(get(object, 'a.b.c', 'default'))
// => 'default'
/**
* flatMap 是先 map 再 flat
* @param {Function} callback - 处理函数
* @param {Any} thisArg - 处理时候的this
*/
Array.prototype.flatMap = function (callback, thisArg) {
if (typeof callback !== "function") throw new TypeError();
_this = Object(thisArg || this);
console.log(_this)
let res = [];
for (let i = 0; i < _this.length; i++) {
const mapped = callback(_this[i]);
if (Array.isArray(mapped)) res.push(...mapped);
else res.push(mapped);
}
return res;
}
const arr = [1, 2, 3, 4];
console.log(arr.flatMap((x) => x + 1));
console.log(arr.flatMap((x) => [x, x * 2]));
const once = (func) => {
let result;
let revoked = false;
return function (...args) {
if (revoked) return result;
revoked = true;
result = func.apply(this, args); // 保留 this
return result;
}
};
// 无限累加的 sum
// 利用闭包和高阶函数
// 重写valueOf,当一个对象要参与运算或比较时,JS 引擎需要把它转换成原始值(primitive)。这时候会触发一个叫 ToPrimitive,会调用对象的valueOf和toString方法
function sum(...args) {
// 用闭包存储累加和
let total = args.reduce((a, b) => a + b, 0)
// 定义一个可调用的函数
function innerSum(...nextArgs) {
total += nextArgs.reduce((a, b) => a + b, 0)
return innerSum // 返回自己,实现链式调用
}
// 重写 valueOf(或 toString),在需要转为数字时触发
innerSum.valueOf = function () {
return total
}
return innerSum
}
console.log(sum(1)(2)(3) == 6) // true
console.log(sum(1, 2, 3) + sum(4, 5))
console.log(sum(10) * sum(10))
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