1. 防抖
用于搜索框输入 多次连续点击结束后,继续等待x秒,再执行回调
function debounce(func,wait){
let timer = null
return function(...args){
if(timer) clearTimeOut(timer)
timer = setTimeOut(()=>{
func.apply(this,args)
},wait)
}
}
2. 节流
拖拽,x秒内只执行一次回调
function throttle(func,wait){
let timer = null
return function(...args){
if(!timer) {
timer = setTimeOut(()=>{
func.apply(this,args)
},wait)
}
}
}
3.instance of
沿着原型链找
function instanceOf(obj,func){
if(typeof obj !== 'object'){
throw TypeError('not a object')
}
let proto = Object.getPrototypeOf(obj)
while(proto!==null){
if(proto===func.prototype) return true
proto = Object.getPrototypeOf(proto)
}
return false
}
4.深拷贝
function deepClone(obj,map = new WeakMap()){
//判断是否复杂对象
const isComplexObject = (obj) => typeof obj === 'object'&&obj!==null
if (!isComplexObject(obj)) return obj
//判断是否date。正则等特殊对象
const type = Object.prototype.toString.call(obj)
if(type === '[object Date]') return new Date(obj)
if(type === '[object RegExp]') return new RegExp(obj)
//weakMap存储
if(map.has(obj)) return map.get(obj)
//使用obj原型create一个新对象,同时传入obj的所有属性
const desc = Object.getOwnPropertyDescriptors(obj)
const cloneObj = Object.create(Object.getPrototypeOf(obj),desc)
map.set(obj,cloneObj)
for(let key of Reflect.ownKeys(obj)){
cloneObj[key] = (isComplexObject(obj[key])&&typeof obj[key]!=='function')?deepClone(obj[key],map):obj[key]
}
//遍历obj的属性,如果是复杂属性则递归clone,否则直接复制
return cloneObj
}
5.new
function myNew(func,...args){
let obj = Object.create(func.prototype)
let res = func.apply(this,args)
return typeof res === 'object'? res:obj
}
6.call/apply/bind
function call(context=window,...args){
let func = this
let key = Symbol()
context[key]=func
let res = context[key](...args)
delete context[key]
return res
}
function bind(context=window,...args){
let func = this
function boundFn(...innerArgs){
return func.apply(this instanceof boundFn?this:context,args.concat(innerArgs))
}
boundFn.prototype = Object.create(func.prototype)
return boundFn
}
7.promise简版
class MyPromise {
constructor(executor) {
this.status = 'pending'
this.value = undefined
this.reason = undefined
const resolve = (value) => {
if (this.status === 'pending') {
this.status = 'resolved'
this.value = value
}
}
const reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected'
this.reason = reason
}
}
try {
executor(resolve, reject)
} catch (e) {
throw new Error(e)
}
}
then(onFulfilled, onRejected) {
switch (this.status) {
case 'resolved':
onFulfilled && onFulfilled(this.value)
break
case 'rejected':
onRejected && onRejected(this.reason)
break
default:
// 仍是 pending 状态下不做处理(完整版实现会在这里存回调)
break
}
}
}
8.generator实现async await
function generatorToAsync(gen) {
return function (...args) {
const iterator = gen.apply(this, args)
return new Promise((resolve, reject) => {
function go(key, arg) {
let res
try {
res = iterator[key](arg)
} catch (err) {
return reject(err)
}
const { value, done } = res
if (done) {
return resolve(value)
} else {
return Promise.resolve(value).then(
val => go('next', val),
err => go('throw', err)
)
}
}
go('next', undefined)
})
}
}
9.数组flatten
function flatten(arr){
let res = []
for(let i=0;i<arr.length;i++){
res = res.concat(Array.isArray(arr[i]?flatten(arr[i]):arr))
}
}
function flatten(arr){
return arr.reduce((pre,cur)=>{
return pre.concat(Array.isArray(cur?flatten(arr[i]):arr))
},[]
)
}
//es6 flat
arr.flat(Infinity)
while(arr.some(Array.isArray)){
arr = [].concat(...arr)
}
10.发布订阅
class Emitter{
constructor(){
this.eventList = {}
}
on(event,fn){
if(!this.eventList[event]){
this.eventList[event] = []
}
this.eventList[event].push(fn)
}
off(event,fn){
if(!this.eventList[event]){
return
}
this.eventList[event] = this.eventList[event].filter(cb=>cb!==fn)
if(!this.eventList[event].length){
// 如果没有事件监听它了,就直接删除这个事件类型
delete this.eventList[event];
}
}
emit(event,...args){
if(!this.eventList[event]){
return
}
this.eventList[event].forEach(fn=>fn(...args))
}
}
11.promise.all .allsetteled .race
// Promise.all
Promise.all = function(promises) {
const res = []
let count = 0
const n = promises.length
return new Promise((resolve, reject) => {
if (n === 0) return resolve(res)
for (let i = 0; i < n; i++) {
Promise.resolve(promises[i])
.then(value => {
res[i] = value
count++
if (count === n) resolve(res)
})
.catch(reject)
}
})
}
// Promise.allSettled
Promise.allSettled = function(promises) {
const res = []
let count = 0
const n = promises.length
return new Promise(resolve => {
if (n === 0) return resolve(res)
for (let i = 0; i < n; i++) {
Promise.resolve(promises[i])
.then(value => res[i] = { status: 'fulfilled', value })
.catch(reason => res[i] = { status: 'rejected', reason })
.finally(() => {
count++
if (count === n) resolve(res)
})
}
})
}
// Promise.race
Promise.race = function(promises) {
const n = promises.length
return new Promise((resolve, reject) => {
if (n === 0) return
for (let i = 0; i < n; i++) {
Promise.resolve(promises[i])
.then(resolve)
.catch(reject)
}
})
}
12.array
Array.prototype.myFilter(cb,context=window){
//filter根据cb返回true/false,过滤掉false的数组项
const res = []
const arr = this
for(let i=0;i<this.length;i++){
if(cb.apply(context,[this[i],i,this])) res.push(arr[i])
}
return res
}
Array.prototype.myMap(cb,context=window){
const res = []
for(let i=0;i<this.length;i++){
res.push(cb.apply(context,[this[i],i,this]))
}
return res
}
Array.prototype.myReduce(cb,pre){
//reduce的cb有pre,cur两个参数
let res = pre?pre:this[0]
let startIndex = pre?1:0
for(let i=startIndex;i<this.length;i++){
cb.call(null,res,this[i],i,this)
}
return res
}
13.大数相加
function add(a ,b) {
let res = ''
let carry = 0
let m = num1.length - 1
let n = num2.length - 1
while (m >= 0 || n >= 0 || carry > 0) {
let cur = (m >= 0 ? parseInt(num1[m]) : 0) + (n >= 0 ? parseInt(num2[n]) : 0) + carry
carry = cur >= 10 ? 1 : 0
res = cur % 10 + res
m--
n--
}
return res
}
console.log(add('3782647863278468012934670', '23784678091370408971329048718239749083'))
14.撤销重做历史状态管理
class UndoRedoStack {
constructor(initialState = null, maxHistory = 100) {
this.past = [];
this.present = initialState;
this.future = [];
this.maxHistory = maxHistory;
}
setPresent(state) {
if (this.present !== null && this.present !== state) {
this.past.push(this.present);
if (this.past.length > this.maxHistory) this.past.shift();
}
this.present = state;
this.future = [];
}
undo() {
if (this.past.length === 0) return;
this.future.push(this.present);
this.present = this.past.pop();
}
redo() {
if (this.future.length === 0) return;
this.past.push(this.present);
this.present = this.future.pop();
}
get canUndo() { return this.past.length > 0; }
get canRedo() { return this.future.length > 0; }
}
15.倒计时
import React, { useState, useEffect } from "react";
// 自定义 Hook 的名称必须以 use 开头
function useCountdown(initialTime) {
// 使用 useState Hook 来创建一个时间和一个更新时间的函数
const [time, setTime] = useState(initialTime);
// 使用 useState Hook 来创建一个状态和一个更新状态的函数
const [isRunning, setIsRunning] = useState(false);
// 使用 useEffect Hook 来实现倒计时的逻辑
useEffect(() => {
// 如果状态为运行并且时间大于零
if (isRunning && time > 0) {
// 设置一个定时器,每隔一秒更新一次时间
const timerId = setTimeout(() => {
setTime(time - 1);
}, 1000);
// 返回一个清理函数,用于取消定时器
return () => {
clearTimeout(timerId);
};
}
// 如果状态为停止或者时间等于零
else {
// 设置状态为停止
setIsRunning(false);
}
}, [isRunning, time]); // 依赖于状态和时间
// 定义一个开始倒计时的函数
function start() {
setIsRunning(true);
}
// 定义一个暂停倒计时的函数
function pause() {
setIsRunning(false);
}
// 定义一个重置倒计时的函数
function reset() {
setTime(initialTime);
setIsRunning(false);
}
// 返回时间和控制函数
return [time, start, pause, reset];
}
16.数组去重
//1 set
function uniq(arr){
return [...new Set(arr)]
}
//2 filter
function uniq(arr){
return arr.filter((cur,i,arr)=>{
i===arr.indexOf(cur)
})
}
//3 相邻元素
function uniq(arr){
arr.sort((a, b) => a - b);
let newArr = []
for(let i=0;i<arr.length;i++){
if(arr[i]===arr[i-1]) continue
newArr.push(arr[i])
}
return newArr
}
//对象数组
function uniq(arr){
const arr1 = arr.map(JSON.stringify)
const arr2 = [...new Set(arr1)]
return arr2.map(JSON.parse)
}
//高级版 对象数组去重
/**
* 递归地对对象的属性进行字母顺序排序。
*
* 该函数接受一个对象并返回一个新的对象,其属性按字母顺序排序。
* 如果对象包含嵌套对象或数组,它们也会被递归地按字母顺序排序。
*
* @param obj - 要排序的对象。
* @returns 返回一个属性按字母顺序排序的新对象。如果输入不是对象,则直接返回输入。
*/
function sortObject(obj) {
// 检查 obj 是否为对象且不为 null,如果不是,直接返回 obj
if (typeof obj !== 'object' || obj === null) {
return obj;
}
// 如果 obj 是数组,对每个元素递归调用 sortObject 并返回新数组
if (Array.isArray(obj)) {
return obj.map(sortObject);
}
// 创建一个新对象以存储排序后的属性
const sortedObj = {};
// 获取对象的键,按字母顺序排序,并对每个属性递归调用 sortObject,将结果存储在新对象中
Object.keys(obj).sort().forEach(key => {
sortedObj[key] = sortObject(obj[key]);
});
// 返回按字母顺序排序属性的对象
return sortedObj;
}
let arr = [
{ id: { n: 1 }, name: '张三' },
{ name: '张三', id: { n: 1 } },
{ id: { n: 2 }, name: '李四' }
];
const arr1 = arr.map(item => JSON.stringify(sortObject(item)));
const arr2 = [...new Set(arr1)];
const result = arr2.map(item => JSON.parse(item));
console.log(result);
17.柯里化
//高阶函数,利用了闭包对函数做了一层封装
function curry(fn) {
return function curried(...args){
if(args.length>=fn.length){
return fn(...args)
}else{
return function (...newArgs){
return curried(...args,...newArgs)
}
}
}
}
18.add
实现add(1)(2,3)(4)() 和add(1)(2,3)(4)
function add(...args){
let sum = args.reduce((pre,cur)=>pre+cur)
function fn(...newArgs){
if(newArgs.length===0) return sum
sum += newArgs.reduce((pre,cur)=>pre+cur)
return fn
}
return fn
}
19.compose和pipe
组合多个函数,从右到左,比如:compose(f, g, h) 最终得到这个结果 (...args) => f(g(h(...args))).
// 从右到左:compose
function compose(fns) {
if (fns.length === 0) return v => v
if (fns.length === 1) return fns[0]
return fns.reduce((a, b) => (...args) => a(b(...args)))
}
// 从左到右:pipe
function pipe(fns) {
if (fns.length === 0) return v => v
if (fns.length === 1) return fns[0]
return fns.reduceRight((a, b) => (...args) => b(a(...args)))
}
20.settimeout和setInterval
//setTimeOut模拟setInterval
function mySetInterval(fn,wait){
let timer = null
function loop(){
fn()
timer = setTimeout(loop,wait)
}
timer = setTimeout(loop,wait)
return {
cancel:()=>clearTimeout(timer)
}
}
//setInterval模拟setTimeOut
function mySetTimeOut(fn,wait){
let timer = setInterval(()=>{
fn();
clearInterval(timer)
},wait)
}
21.并发池
class Scheduler{
constructor(limit){
this.limit = limit
this.queue = []
this.count = 0
}
//当一个request完成后需要加入新的
runNext(){
if(!this.queue.length||this.count>=this.limit) return
this.count++
this.queue.shift()().then(()=>{
this.count--
this.runNext()
})
}
add(fn,wait){
//队列中加入新的fn
const task = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
fn()
resolve()
},wait)
})
}
this.queue.push(task)
this.runNext()
}
}
22.计算一个对象的层数
function getDepth(obj){
let res = 1
function dfs(obj,level){
if(typeof obj === 'object'){
for(let key in obj){
if(typeof obj[key] === 'object'){
dfs(obj[key],level+1)
}else{
res = Math.max(res,level+1)
}
}
}else{
res = Math.max(res,level)
}
}
dfs(obj,0)
return res
}
23.对象的扁平化
const obj = {
a: {
b: 1,
c: 2,
d: {e: 5}
},
b: [1, 3, {a: 2, b: 3}],
c: 3
}
flatten(obj)
// {
// 'a.b': 1,
// 'a.c': 2,
// 'a.d.e': 5,
// 'b[0]': 1,
// 'b[1]': 3,
// 'b[2].a': 2,
// 'b[2].b': 3
// c: 3
// }
function flattenObj(obj){
let res = {}
const isObject = (obj) => typeof obj ==='object' && obj !== null
function dfs(cur,pre){
if(isObject(cur)){
if(Array.isArray(cur)){
cur.forEach((item,index)=>{
dfs(item,`${pre}[${index}]`)
})
}else{
for(let key in cur){
dfs(cur[key],`${pre}${pre?'.':''}${key}`)
}
}
}else{
res[pre] = cur
}
}
dfs(obj,'')
return res
}
24.实现(a == 1 && a == 2 && a == 3)为true
const a = {
i:0,
toString(){
return ++this.i
}
}
let i = 0
Object.defineProperty(window,'a',{
get:()=>{
return ++i
}})
//proxy
let value = 1
const a = new Proxy({},{
get:function(target,name){
return value++
}
})
25.将DOM转化成树结构对象
function reverseDom(dom){
const obj = {
tag:dom.tagName,
children:[],
attrs:{}
}
for (let attr of dom.attributes) {
obj.attrs[attr.name] = attr.value;
}
dom.childNodes.forEach((child)=>{
obj.children.push(reverseDom(child))
})
return obj
}
26.将树结构转换为DOM
function treeToDom(vNode){
if(typeof vNode==='number') return String(vNode)
if(typeof vNode==='string') return vNode
const dom = document.createElement(vNode.tag)
//转换children。attr。
if(vNode.attrs){
for(let key in vNode.attrs){
dom.setAttribute(key,vNode.attrs[key])
}
}
vNode.children.forEach((child)=>{
dom.appendChild(treeToDom(child))
})
return dom
}
27.判断一个对象有环引用
var obj = {
a: {
c: [
1, 2
]
},
b: 1
}
obj.a.c.d = obj
console.log(cycleDetector(obj)) // true
function cycleDetector(obj){
const set = new Set()
function findCircle(obj){
if(obj&&typeof obj === 'object'){
if(set.has(obj)) return true
set.add(obj)
for(let key in obj){
if(findCircle(obj[key])) return true
}
}
return false
}
return findCircle(obj)
}
28.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”).eat(“supper”).sleepFirst(5)
//等待5秒
Wake up after 5
Hi This is Hank!
Eat supper
class LazyMan{
constructor(name){
this.name = name
this.queue = []
const task = ()=>{
console.log(`这个人叫做${name}!`)
this.next()
}
this.queue.push(task)
setTimeout(() => {
this.next()
}, 0);
}
next(){
const task = this.queue.shift()
task && task()
}
eat(meal){
const task = ()=>{
console.log(`吃了${meal}!`)
this.next()
}
this.queue.push(task)
return this
}
sleepWrapper(wait,flag){
const task = ()=>setTimeout(()=>{
console.log(`睡了${wait}秒`)
this.next()
},wait*1000)
//flag为true就是first
if(flag){
this.queue.unshift(task)
}else{
this.queue.push(task)
}
}
sleep(wait){
this.sleepWrapper(wait,false)
return this
}
sleepFirst(wait){
this.sleepWrapper(wait,true)
return this
}
}
29.Promise A+
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise{
constructor(executor){
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value)=>{
if(this.status!==PENDING) return
this.status = FULFILLED
this.value = value
setTimeout(() => {
this.onFulfilledCallbacks.forEach((fn)=>fn(value))
});
}
const reject = (reason)=>{
if(this.status!==PENDING) return
this.status = REJECTED
this.reason = reason
setTimeout(() => {
this.onRejectedCallbacks.forEach((fn)=>fn(reason))
});
}
try{
executor(resolve,reject)
}catch(e){
reject(e)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r; };
const promise2 = new MyPromise((resolve,reject)=>{
if(this.status === FULFILLED){
setTimeout(() => {
try{
const x = onFulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
});
}else if(this.status===REJECTED){
setTimeout(() => {
try{
const x = onRejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
});
}else{
this.onFulfilledCallbacks.push((value) => {
try {
const x = onFulfilled(value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbacks.push((reason) => {
try {
const x = onRejected(reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
})
return promise2
}
catch(onRejected){
return this.then(null,onRejected)
}
finally(cb){
return this.then(
value => MyPromise.resolve(cb()).then(()=>value),
reason => MyPromise.resolve(cb()).then(()=>{throw reason})
)
}
static resolve(value){
return new MyPromise(resolve=>resolve(value))
}
static reject(reason){
return new MyPromise((_,reject)=>reject(reason))
}
static all(iterable){
return new MyPromise((resolve,reject)=>{
const res = []
const arr = Array.from(iterable)
if(arr.length===0) return resolve([])
let count = 0
arr.forEach((p,i)=>{
MyPromise.resolve(p).then(
val=>{
res[i] = val
count++
if(count===arr.length) resolve(res)
},
reject
)
})
})
}
static race(iterable){
return new MyPromise((resolve,reject)=>{
for(const p of iterable){
MyPromise.resolve(p).then(resolve,reject)
}
})
}
}
function resolvePromise(promise2,x,resolve,reject){
if(promise2===x) {
return reject(new TypeError('有环'))
}
if((x!==null)&&(typeof x === 'object'||typeof x === 'function')){
let called = false;
try{
let then = x.then
if(typeof then === 'function'){
then.call(x,
y=>{
if(called) return
called = true
resolvePromise(promise2,y,resolve,reject)
},
r => {
if (called) return;
called = true;
reject(r);
})
}else{
resolve(x)
}
}catch(e){
if (called) return;
called = true;
reject(e);
}
}else{
resolve(x)
}
}
30.常用hooks:useState、useEffect、useMemo、useRef
let hooks = []
let index = 0
function useState(initialValue){
let currentIndex = index
hooks[currentIndex] = hooks[currentIndex]??initialValue
const setState = (newValue)=>{
hooks[currentIndex] = typeof newValue === 'function'? newValue(hooks[currentIndex]):newValue
render()
}
index++
return [hooks[currentIndex],setState]
}
function useEffect(fn, deps) {
const currentIndex = index
const preDeps = hooks[currentIndex]
let hasChanged = false
if (deps === undefined) {
// 无依赖 -> 每次执行
hasChanged = true
} else if (!preDeps) {
// 首次渲染 -> 执行
hasChanged = true
} else {
// 比较依赖数组
hasChanged = deps.some((cur, i) => !Object.is(preDeps[i], cur))
}
if (hasChanged) {
queueMicrotask(fn) // 模拟异步执行
hooks[currentIndex] = deps
}
index++
}
function useRef(initialValue){
const currentIndex = index
if(!hooks[currentIndex]){
hooks[currentIndex]={current:initialValue}
}
index++
return hooks[currentIndex]
}
function useMemo(factory,deps){
const currentIndex = index
const pre = hooks[currentIndex]
if(pre){
const {value:preValue,deps:preDeps} = pre || {}
if(deps&&preDeps){
let hasChanged = false
if(deps.some((cur,i)=>!Object.is(cur,preDeps[i]))){
hasChanged = true
}
if(hasChanged){
index++
return preValue
}
}
}
hooks[currentIndex] = {
deps,
value:factory()
}
index++
return hooks[currentIndex].value
}
31.useRequest
function useRequest<T>(
requestFn : (...args:any[])=>Promise<T>,
options?:{
manual?:boolean,
deps?:any[]
}
){
const {manual = false,deps = []} = options||{}
const [error,setError] = useState<unknown>(null)
const [data,setData] = useState<T|null>(null)
const [loading,setLoading] = useState(false)
const run = useCallback(async(...args:any[])=>{
setError(null)
setLoading(true)
try{
const res = await requestFn(...args)
setData(res)
return res
}catch(e){
setError(e)
throw e;
}finally{
setLoading(false)
}
},[requestFn])
useEffect(()=>{
if(!manual) run()
},[manual,...deps])
return {data,error,loading,run}
}
高阶版,包含轮询、取消、回调
export interface UseRequestOptions<TParams extends any[], TData> {
manual?: boolean;
deps?: any[];
defaultParams?: TParams;
onSuccess?: (data: TData) => void;
onError?: (error: any) => void;
pollingInterval?: number; // 轮询间隔
}
export function useRequest<TData = any, TParams extends any[] = any[]>(
service: (...args: TParams) => Promise<TData>,
options?: UseRequestOptions<TParams, TData>
) {
const {
manual = false,
deps = [],
defaultParams = [] as unknown as TParams,
onSuccess,
onError,
pollingInterval,
} = options || {};
const [data, setData] = useState<TData | null>(null);
const [error, setError] = useState<any>(null);
const [loading, setLoading] = useState(false);
const timerRef = useRef<number | null>(null);
const abortController = useRef<AbortController | null>(null);
const run = useCallback(async (...args: TParams) => {
if (abortController.current) {
abortController.current.abort(); // 取消前一次请求
}
abortController.current = new AbortController();
setLoading(true);
setError(null);
try {
const res = await service(...args);
setData(res);
onSuccess?.(res);
return res;
} catch (err: any) {
if (err.name === 'AbortError') return;
setError(err);
onError?.(err);
throw err;
} finally {
setLoading(false);
}
}, [service]);
// 自动执行
useEffect(() => {
if (!manual) {
run(...defaultParams);
}
}, deps);
// 轮询机制
useEffect(() => {
if (!pollingInterval) return;
const loop = async () => {
await run(...defaultParams);
timerRef.current = window.setTimeout(loop, pollingInterval);
};
loop();
return () => {
if (timerRef.current) clearTimeout(timerRef.current);
};
}, [pollingInterval]);
const refresh = useCallback(() => {
if (data) run(...defaultParams);
}, [data, run]);
return {
data,
error,
loading,
run,
refresh,
cancel: () => abortController.current?.abort(),
};
}
32.raf实现计时器
function RAFSetTimeOut(fn,delay=0){
let start = performance.now()
let timer;
let stopped = false
function tick(now){
if(stopped) return
if(now-start>=delay){
fn()
}else{
timer = requestAnimationFrame(tick)
//raf的回调的参数是一个 DOMHighResTimeStamp 参数,用于表示上一帧渲染的结束时间(基于 time origin 的毫秒数),接近于performance.now()
}
}
timer = requestAnimationFrame(tick)
//clear
return () => {
stopped = true;
if (timer != null) cancelAnimationFrame(timer);
};
}
function RAFSetInterval(fn,interval){
let start = performance.now()
let lastFired = start;
let timer = null
let stopped = false;
function tick(now){
if(stopped) return
if(now-lastFired>=interval){
start = now
lastFired += interval
}
timer = requestAnimationFrame(tick)
}
timer = requestAnimationFrame(tick)
return ()=>{
stopped = true
if(timer) cancelAnimationFrame(timer)
}
}
33.观察者模式:DOM 事件、MutationObserver
//被观察者
class Subject{
constructor(name,state){
this.name = name
this.observers = []
}
addObserver(obs){
this.observers.push(obs)
}
removeObserver(obs){
this.observers.filter((o)=>o!==obs)
}
notify(data){
this.observers.forEach((obs)=>obs.update(data))
}
}
class Observer{
constructor(name){
this.name = name
}
update(data){
console.log(`${this.name}在${data}`)
}
}
//可以添加一次性观察,异步执行回调功能
34.单例模式
//常见单例:windows。document。redux。全局 EventBus / PubSub
class Singleton{
constructor(){
if(Singleton.instance){
return Singleton.instance
}
this.name = name
Singleton.instance = this
}
}
//高阶函数把任意类封装为单例
function Singletonify(Class) {
let instance;
return function(...args) {
if (!instance) {
instance = new Class(...args);
}
return instance;
};
}
35.ajax
function ajax(method,url,params,fn){
const xhr = new XMLHttpRequest()
//如果是get,拼接params
method = method.toUpperCase()
let pair = []
for(let key in params){
pair.push(key+'=' + params[key])
}
let getData = pair.join('&')
if(method==='GET'){
url = url+'?'+getData
}
xhr.open(method,url)
//如果是post,需要加请求体
let postData = null
if(method==='POST'){
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded')
postData = getData
}
xhr.send(postData)
xhr.onreadystatechange = function (){
if(this.readyState === 4){
// 返回的应该是一个对象,这样客户端更好渲染
fn(JSON.parse(xhr.responseText));
}
}
}
36.继承
//es5继承
function Parent () {
this.name = 'parent';
this.play = [1, 2, 3];
}
function Child() {
Parent.call(this);
this.type = 'child';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
const c = new Child()
console.log(c.name)
//es6继承
class Parent {
constructor(value) {
this.val = value
}
getValue() {
console.log(this.val)
}
}
class Child extends Parent {
constructor(value) {
super(value)
this.val = value
}
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true
// class 实现继承的核心在于使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super,因为这段代码可以看成 Parent.call(this, value)。
37.入参是tasks,timeout,retries。一次发任意多个请求,如果有失败就重新执行,成功则返回一个promise,直到所有的都成功;超时或者超出最大重试次数抛出异常
function runTasks(tasks, timeout = 3000, retries = 3) {
// 超时包装器
const withTimeout = (task, timeout) => {
return Promise.race([
task(),
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeout))
]);
};
return new Promise(async (resolve, reject) => {
let attempt = 0;
let pendingTasks = [...tasks]; // 当前要执行的任务列表
while (pendingTasks.length && attempt <= retries) {
attempt++;
console.log(`第 ${attempt} 次尝试执行 ${pendingTasks.length} 个任务`);
// 执行所有任务
const results = await Promise.allSettled(
pendingTasks.map(task => withTimeout(task, timeout))
);
const failed = [];
const successes = [];
results.forEach((res, i) => {
if (res.status === 'fulfilled') {
successes.push(res.value);
} else {
failed.push(pendingTasks[i]); // 失败任务保留,稍后重试
}
});
if (failed.length === 0) {
return resolve(successes);
}
console.warn(`${failed.length} 个任务失败,准备重试...`);
// 达到最大重试次数
if (attempt >= retries) {
return reject(new Error(`任务失败,已达到最大重试次数 ${retries}`));
}
pendingTasks = failed; // 仅重试失败任务
}
});
}
38.有一个任务队列,每项是一个函数,实现一个方法使得每项任务间隔时间超过150ms,且该任务队列一直在不停的添加任务
class TaskQueue {
constructor(interval = 150) {
this.queue = [];
this.running = false;
this.interval = interval;
}
add(task) {
// task 是一个函数,可能返回 Promise
this.queue.push(task);
if (!this.running) this.run();
}
async run() {
this.running = true;
while (this.queue.length) {
const task = this.queue.shift();
try {
await task(); // 任务可能是异步的
} catch (e) {
console.error('任务出错:', e);
}
await new Promise(res => setTimeout(res, this.interval));
}
this.running = false;
}
}
39.洗牌
function shuffle(arr) {
const res = arr.slice() // 不修改原数组
for (let i = res.length - 1; i > 0; i--) {
// 生成 [0, i] 之间的随机索引
const j = Math.floor(Math.random() * (i + 1))
// 交换位置
;[res[i], res[j]] = [res[j], res[i]]
}
return res
}
40.arrToTree
const arr = [
{ id: 1, name: '根节点', parentId: null },
{ id: 2, name: '子节点1', parentId: 1 },
{ id: 3, name: '子节点2', parentId: 1 },
{ id: 4, name: '子节点1-1', parentId: 2 },
{ id: 5, name: '子节点1-2', parentId: 2 },
]
function arrToTree(arr,treeRoot = null){
const tree = []
const map = new Map()
arr.forEach((node)=>{
map.set(node.id,{
...node,
children:[]
})
})
arr.forEach((item)=>{
const node = map.get(item.id)
if(node.parentId===treeRoot){
tree.push(node)
}else{
const parent = map.get(node.parentId)
if(parent){
parent.children.push(node)
}
}
})
return tree
}
41. 快速排序
function sortArray(nums) {
if(nums.length<=1) return nums;
let pivot = Math.floor(Math.random() * nums.length)
const left = []
const right = []
const middle = []
for(let i=0;i<nums.length;i++){
if(nums[i]>nums[pivot]){
right.push(nums[i])
}else if(nums[i]<nums[pivot]){
left.push(nums[i])
}else middle.push(nums[i])
}
return [...sortArray(left),...middle,...sortArray(right)]
};
42.冒泡排序
function sortArray(nums) {
//冒泡
const n = nums.length
for (let i = 0; i < n - 1; i++) {
for (let j = 0; j < n - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
[nums[j], nums[j + 1]] = [nums[j + 1], nums[j]]
}
}
}
return nums
};
42.插入排序
function sortArray(nums) {
//插入,每次把元素放进已排序区间中的正确位置,适合基本有序
const n = nums.length
for (let i = 1; i < n; i++) {
let key = nums[i]
let j = i-1
while(j>=0&&nums[j]>key){
nums[j+1]=nums[j]
j--
}
nums[j+1]=key
}
return nums
};
43.选择排序
function sortArray(nums) {
//选择,每次找未排序区间的最小值
const n = nums.length
for (let i = 0; i < n - 1; i++) {
let min = i
for(let j=i+1;j<n;j++){
if(nums[j]<nums[min]){
min = j
}
}
[nums[min],nums[i]]=[nums[i],nums[min]]
}
return nums
};
44 pick
type MyPick<T,K extends keyof T> = {
[P in K]:T[P]
}
45 omit
type MyOmit<T,K extends keyof T> = {
[P in Exclude<keyof T,K>] : T[P]
}
46 红绿灯
const traffic_light = (color, duration) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('traffic_light', color);
resolve()
}, duration)
})
}
const main = () => {
Promise.resolve()
.then(() => traffic_light('green', 5000))
.then(() => traffic_light('yellow', 1000))
.then(() => traffic_light('red', 2000))
.then(() => {
main()
})
}
main();
实现一个sleep函数
写一个点赞组件,按钮点赞和取消
怎么实现:用户右滑时,逐步展示操作步骤图(每张图代表一个步骤),左滑时退回上一步,类似一个可控的进度条式动画。
实现组件,一天有24小时,prop中传进来的值是想要剔除的时间段,比如[1,2]和[5,24],你会怎么设计这个组件
实现一个异步执行的wait
ts实现omit infer
实现一个第一个不会触发的useeffect
怎么实现一个方法使得localstorage 有过期时间