防抖
/**
* 防抖:一定的时间内,多次触发同一个事件,只执行最后一次
*/
function debounce(fn, delay) {
let timer = null
// 这里的 this 是 window,因为 debounce 是在 window 上调用的
return function () {
// 这里的 this 是 <input />
if (timer) clearTimeout(timer)
timer = setTimeout(() => { // 这里因为使用了 箭头函数 ,所以用了外部的 this
// 函数调用
fn.apply(this)
clearTimeout(timer)
}, delay)
}
}
let input = document.querySelector('input')
input.oninput = debounce(function () { // 回调函数声明
console.log(this.value)
}, 1000)
节流
/**
* 节流:一个时间段内最多触发一次
*/
function throttle(fn, delay) {
let timer = null
return function () {
if (timer) return
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null;
}, delay)
}
}
红绿灯交替
// 模拟一个红绿灯变化,红灯 1 秒,绿灯 1 秒,黄灯 1 秒,然后循环
function red() {
console.log('red')
}
function green() {
console.log('green')
}
function yellow() {
console.log('yellow')
}
function light(cb) {
return new Promise((resolve) => {
setTimeout(() => {
cb()
resolve()
}, 1000)
})
}
function start() {
light(red).then(() => {
return light(green)
}).then(() => {
return light(yellow)
}).finally(() => {
start()
})
}
start()
深拷贝
/**
* 1.基本类型数据:赋值就是深拷贝了。
* 2.引用类型数据:内部的引用类型数据全都独立开一块内存空间。数据之间完全互不干扰。
* 3. 深拷贝的实现方式:
* a. 递归
* b. JSON.parse(JSON.stringify())
* c. structuredClone() const clonedObj = structuredClone(originalObj); 法拷贝函数、Symbol、DOM 节点等特殊对象
*/
function deepClone(obj, hash = new WeakMap()) {
if (!obj || typeof obj !== 'object') return obj
if (hash.has(obj)) return hash.get(obj)
const newObj = Array.isArray(obj) ? [] : {}
hash.set(obj, newObj)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key], hash)
}
}
return newObj
}
浅拷贝
/**
* 1. 基本数据类型:不存在浅拷贝概念
* 2. 对象的浅拷贝是指创建一个新对象,新对象与原对象共享基本数据类型的属性值,但对于引用类型的属性,只复制其引用地址而非深层内容
* 浅拷贝仅复制对象的第一层属性,对于嵌套对象仍共享引用
* 3. 浅拷贝的实现方式:
* a. for...in 循环
* b. Object.assign({},{a:1,b:{c:2}})
* c. 展开运算符 ...
*/
function shallowCopy(obj) {
if (!obj || typeof obj !== 'object') return obj
const newObj = Array.isArray(obj) ? [] : {}
for (let key in obj) {
if (obj.hasOwnProperty(key)) { // obj.hasOwnProperty(key) 用于判断对象 obj 是否 自身拥有 指定属性 key ,而不是从原型链继承的属性。
newObj[key] = obj[key]
}
}
return newObj
}
树转数组
树的层序遍历
const obj = {
id: 1,
name: '部门A',
children: [
{
id: 2,
name: '部门B',
children: [
{ id: 4, name: '部门D' },
{ id: 5, name: '部门E' }
]
},
{
id: 3,
name: '部门C',
children: [{ id: 6, name: '部门F' }]
}
]
}
function convert1(root) {
const nodeToParent = new Map() // key 当前节点 value 父节点
const arr = []
// 广度优先遍历, queue
const queue = []
queue.push(root) // 根节点 入队
while (queue.length > 0) {
const curNode = queue.shift() // 出队
if (curNode == null) break
const { id, name, children = [] } = curNode
// 创建数组 item 并 push
const parentNode = nodeToParent.get(curNode)
const parentId = parentNode?.id || 0
const item = { id, name, parentId }
arr.push(item)
// 子节点入队
children.forEach(child => {
// 映射 parent
nodeToParent.set(child, curNode)
// 入队
queue.push(child)
})
}
return arr
}
数组转树
const arr = [
{ id: 2, name: '部门B', parentId: 1 },
{ id: 4, name: '部门D', parentId: 2 },
{ id: 5, name: '部门E', parentId: 2 },
{ id: 6, name: '部门F', parentId: 3 },
{ id: 3, name: '部门C', parentId: 1 },
{ id: 1, name: '部门A', parentId: 0 },
]
function handle(arr) {
const map = new Map();
let root = null;
// 先为每个节点创建newNode并存储到map,确保操作的是新节点
arr.forEach(item => {
const newNode = { id: item.id, name: item.name, children: [] };
map.set(item.id, newNode);
if (item.parentId === 0) {
root = newNode; // 根节点指向newNode
}
});
// 再为每个节点添加子节点(关联newNode)
arr.forEach(item => {
const parentNode = map.get(item.parentId);
if (parentNode) {
parentNode.children.push(map.get(item.id)); // 子节点是newNode
}
});
return root;
}
console.log(handle(arr))
Promise
class Commitment {
static PENDING = '待定'; static FULFILLED = '成功'; static REJECTED = '拒绝';
constructor(func) {
this.status = Commitment.PENDING;
this.result = null;
this.resolveCallbacks = [];
this.rejectCallbacks = [];
func(this.resolve.bind(this), this.reject.bind(this));
}
resolve(result) {
setTimeout(() => {
if (this.status === Commitment.PENDING) {
this.status = Commitment.FULFILLED;
this.result = result;
this.resolveCallbacks.forEach(callback => {
callback(result);
});
}
});
}
reject(result) {
setTimeout(() => {
if (this.status === Commitment.PENDING) {
this.status = Commitment.REJECTED;
this.result = result;
this.rejectCallbacks.forEach(callback => {
callback(result);
});
}
});
}
then(onFULFILLED, onREJECTED) {
if (this.status === Commitment.PENDING) {
this.resolveCallbacks.push(onFULFILLED);
this.rejectCallbacks.push(onREJECTED);
}
if (this.status === Commitment.FULFILLED) {
setTimeout(() => {
onFULFILLED(this.result);
});
}
if (this.status === Commitment.REJECTED) {
setTimeout(() => {
onREJECTED(this.result);
});
}
}
}
Promise.race
// 有一个状态改变的时候就返回
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("SSSSSS")
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("XXXXXX")
}, 2000)
})
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((item) => {
// Promise.resolve(item) 确保 item 是一个 Promise 对象。如果 item 本身就是 Promise,它会直接返回该 Promise;如果 item 不是 Promise,它会将 item 包装成一个已 fulfilled 状态的 Promise 。
Promise.resolve(item).then(resolve, reject)
})
})
}
Promise.race([p1, p2]).then(res => console.log(res)) // "SSSSSS"
Promise.all
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("SSSSSS")
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("XXXXXX")
}, 2000)
})
Promise.all = function(promises) {
let res = []
let count = 0
return new Promise((resolve, reject) => {
promises.forEach((item, index) => {
Promise.resolve(item).then((ans) => {
res[index] = ans
count ++
if(promises.length === count) return resolve(res)
},() => {
return reject(res)
})
})
})
}
Promise.all([p1, p2]).then(res => console.log(res))//·['SSSSSS', 'XXXXXX']
new
function customNew(constructor, ...args) {
const obj = {}
obj.__proto__ = constructor.prototype
constructor.apply(obj, args)
return obj
}
const obj = customNew(Foo, 'zhangsan')
LRU
class LRU {
constructor(len) {
this.len = len
this.map = new Map()
}
set(key, value) {
if (this.map.has(key)) {
this.map.delete(key)
}
this.map.set(key, value)
if (this.map.size > this.len) {
this.map.delete(this.map.keys().next().value)
}
}
get(key) {
if (!this.map.has(key)) return null
const value = this.map.get(key)
this.map.delete(key)
this.map.set(key, value)
return value
}
has(key) {
return this.map.has(key)
}
}
LazyMan
/*
const me = new LazyMan('张三')
me.eat('苹果').eat('香蕉').sleep(5).eat('葡萄') // 打印结果如下:
// '张三 eat 苹果'
// '张三 eat 香蕉'
// (等待 5s)
// '张三 eat 葡萄'
*/
class LazyMan {
constructor(name) {
this.name = name
this.tasks = []
setTimeout(() => {
this.next()
})
}
next() {
const task = this.tasks.shift() // 取出当前 tasks 的第一个任务
if (task) task()
}
eat(food) {
const task = () => {
console.info(`${this.name} eat ${food}`)
this.next() // 立刻执行下一个任务
}
this.tasks.push(task)
return this // 链式调用
}
sleep(seconds) {
const task = () => {
console.info(`${this.name} 开始睡觉`)
setTimeout(() => {
console.info(`${this.name} 已经睡完了 ${seconds}s,开始执行下一个任务`)
this.next() // xx 秒之后再执行下一个任务
}, seconds * 1000)
}
this.tasks.push(task)
return this // 链式调用
}
}
new LazyMan('Tony')
.eat('lunch')
.eat('dinner')
.sleep(2)
.eat('junk food')
instanceof
export function myInstanceOf(instance, origin) {
if (instance == null) return false // null undefined
const type = typeof instance
if (type !== 'object' && type !== 'function') {
// 值类型
return false
}
let tempInstance = instance // 为了防止修改 instance
while (tempInstance) {
if (tempInstance.__proto__ === origin.prototype) {
return true // 配上了
}
// 未匹配
tempInstance = tempInstance.__proto__ // 顺着原型链,往上找
}
return false
}
getType
// 获取详细的变量类型
function getType(data) {
// 获取到 "[object Type]",其中 Type 是 Null、Undefined、Array、Function、Error、Boolean、Number、String、Date、RegExp 等。
const originType = Object.prototype.toString.call(data)
// 可以直接截取第8位和倒数第一位,这样就获得了 Null、Undefined、Array、Function、Error、Boolean、Number、String、Date、RegExp 等
const type = originType.slice(8, -1)
// 再转小写,得到 null、undefined、array、function 等
return type.toLowerCase()
}
EventBus
/**
* {
* 'key1': [
* { fn: fn1, once: false },
* { fn: fn2, once: true }
* ],
* 'key2': []
* 'key3': []
* }
* }
*/
class EventBus {
constructor() {
this.events = {};
}
on(eventName, callback, isOnce = false) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push({
fn: callback,
once: isOnce
});
}
once(eventName, callback) {
this.on(eventName, callback, true);
}
off(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(item => item.fn !== callback);
}
}
emit(eventName, ...args) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(item => {
item.fn(...args);
return !item.once;
});
} else {
return
}
}
}
domTree
// 访问节点
function visitNode(n) {
if (n instanceof Comment) {
// 注释
console.info('Comment node ----', n.textContent)
}
if (n instanceof Text) {
// 文本
const t = n.textContent?.trim()
if (t) {
console.info('Text node ----', t)
}
}
if (n instanceof HTMLElement) {
// element
console.info('Element node ----', `<${n.tagName.toLowerCase()}>`)
}
}
// 深度遍历
function depthFirstTraverse1(root) {
visitNode(root)
const childNodes = root.childNodes // .childNodes 和 .children 不一样
if (childNodes.length) {
childNodes.forEach(child => {
depthFirstTraverse1(child) // 递归
})
}
}
// 非递归
function depthFirstTraverse2(root) {
const stack = []
// 根节点压栈
stack.push(root)
while (stack.length > 0) {
const curNode = stack.pop() // 出栈
if (curNode == null) break
visitNode(curNode)
// 子节点压栈
const childNodes = curNode.childNodes
if (childNodes.length > 0) {
// reverse 反顺序压栈
Array.from(childNodes).reverse().forEach(child => stack.push(child))
}
}
}
/**
* 广度优先遍历
* @param root dom node
*/
function breadthFirstTraverse(root) {
const queue = [] // 数组 vs 链表
// 根节点入队列
if (root) queue.push(root); // 根节点入队
while (queue.length > 0) {
const curNode = queue.shift(); // 从队列头部取出节点
if (!curNode) continue; // 跳过null节点
visitNode(curNode)
// 子节点入队
const childNodes = curNode.childNodes
if (childNodes.length) {
childNodes.forEach(child => queue.push(child));
}
}
}
curry
/**
* function add(a,b,c){
* return a+b+c;
* }
* add(1,2,3)
* 等价于
* const curryAdd = curry(add);
* curryAdd(1)(2)(3)
*/
function curry(fn) {
// 获取传入函数的参数长度
const length = fn.length
let args = []
function calc(...newArgs) {
// 积累参数
args = [...args, ...newArgs]
if (args.length < length) {
// 参数不够时,返回函数
return calc
} else {
// 参数够的时候,返回值
return fn.apply(this, args.slice(0, length))
}
}
return calc
}
compose
// 组合与管道
/**
* value (1+1)*3 // 6
* function add1(x){
* return x + 1
* }
* function mul3(x){
* return x * 3
* }
* console.log(mul3(add1(1)))
* function compose(f,g){ // 函数从右向左执行
* return function(x){
* return f(g(x))
* }
* }
* console.log(compose(mul3,add1)(1))
*/
function compose(...funs) {
let count = funs.length - 1;
let result = undefined;
return function fn(x) {
if (count < 0) {
return result;
}
result = funs[count--](x)
return fn(result)
}
}
console.log(compose(mul3, add1)(1)) //6
function compose(...funs) {
let callback = function (f, g) {
return function (x) {
return f(g(x))
}
}
let fn = funs[0]
for (let i = 1; i < funs.length; i++) {
fn = callback(fn, funs[i])
}
return fn;
}
console.log(compose(mul3, add1)(1)) //6
call
/*
function introduce(city, country) {
console.log(`I am ${this.name} from ${city}, ${country}.`);
}
const person = { name: 'Bob' };
introduce.call(person, 'New York', 'USA');
*/
Function.prototype.myCall = function (context, ...args) {
// 处理 null/undefined 并转换基本类型为对象
context = context || globalThis;
const ctx = typeof context !== 'object' ? Object(context) : context;
// 使用 Symbol 避免属性冲突
const fnSymbol = Symbol();
ctx[fnSymbol] = this; // this 就是当前的函数
// 直接使用剩余参数
const result = ctx[fnSymbol](...args);
delete ctx[fnSymbol];
return result;
}
bind
/*
返回一个新函数,但不执行,绑定 this 和 部分参数,无法改变箭头函数的 this
function introduce(city, country) {
console.log(`I am ${this.name} from ${city}, ${country}.`);
}
const person = { name: 'Bob' };
const introduceBob = introduce.bind(person);
introduceBob('Paris', 'France');
*/
Function.prototype.MyBind = function (context, ...args) {
const self = this;
context = context ?? globalThis;
const ctx = typeof context !== 'object' ? Object(context) : context;
const fnSymbol = Symbol();
return function (...innerArgs) {
ctx[fnSymbol] = self;
const result = ctx[fnSymbol](...args, ...innerArgs);
delete ctx[fnSymbol];
return result;
}
}
ArrayFlatten
// 数组扁平化
function flatten1(arr) {
const result = []
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
result.push(...flatten1(arr[i]))
} else {
result.push(arr[i])
}
}
return result
}
// console.log(flatten1([1, [2, [3, [4, [5]]]]]))
function flattenArray(arr, depth = Infinity) {
if (!Array.isArray(arr)) {
return [arr];
}
if (arr.length === 0) {
return [];
}
if (depth <= 0) {
return [...arr];
}
const result = [];
for (const item of arr) {
if (Array.isArray(item) && depth > 0) {
result.push(...flattenArray(item, depth - 1));
} else {
result.push(item);
}
}
return result;
}
console.log(flattenArray([1, [2, [3, [4, [5]]]]], 1))
console.log(flattenArray([1, [2, [3, [4, [5]]]]], 2))
console.log(flattenArray([1, [2, [3, [4, [5]]]]], 3))
console.log(flattenArray([1, [2, [3, [4, [5]]]]], 4))
apply
/*
function introduce(city, country) {
console.log(`I am ${this.name} from ${city}, ${country}.`);
}
const person = { name: 'Bob' };
introduce.apply(person, ['London', 'UK']);
*/
Function.prototype.myApply = function (context, args) {
// 处理 null/undefined 并转换基本类型为对象
context = context || globalThis;
const ctx = typeof context !== 'object' ? Object(context) : context;
// 使用 Symbol 避免属性冲突
const fnSymbol = Symbol();
ctx[fnSymbol] = this; // this 就是当前的函数
// 直接使用剩余参数
const result = ctx[fnSymbol](...args);
delete ctx[fnSymbol];
return result;
}
all
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("SSSSSS")
}, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("XXXXXX")
}, 2000)
})
Promise.all = function(promises) {
let res = []
let count = 0
return new Promise((resolve, reject) => {
promises.forEach((item, index) => {
Promise.resolve(item).then((ans) => {
res[index] = ans
count ++
if(promises.length === count) return resolve(res)
},() => {
return reject(res)
})
})
})
}
Promise.all([p1, p2]).then(res => console.log(res))//·['SSSSSS', 'XXXXXX']