Last updated on November 7, 2024 pm
Promise、任务调度器、LRU缓存、深拷贝、new
(1)手写Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| class MyPromise { constructor(executor){ this.state="pending"; this.value=undifined; this.reason=undifined; this.onFulfilledCallbacks=[]; this.onRejectedCallbacks=[]; const resolve=(value)=>{ if(this.state==="pending"){ this.state="fulfilled"; this.value=value; this.onFulfilledCallbacks.forEach(callback=>callback(this.value)) } }; const reject=(reason)=>{ if(this.state==="pending"){ this.state="rejected"; this.reason=reason; this.onRejectedCallbacks.forEach(callback=>callback(this.reason)) } }; try { executor(resolve,reject); }catch(error){ reject(error); } } then(onFulfilled,onRejected){ onFulfilled=typeof onFulfilled==="function"?onFulfilled:value=>value onRejected=typeof onRejected==="function"?onRejected:reason=>{ throw reason} return new MyPromise((resolve,reject)=>{ if(this.state==="fulfilled"){ setTimeout(()=>{ try{ const result=onFulfilled(this.value); resolve(result); }catch(error){ reject(error); } }); } if(this.state==="rejected"){ setTimeout(()=>{ try{ const reason=onRejected(this.reason); reject(reason); }catch(error){ reject(error); } }); } if(this.state==="pending"){ this.onFulfilledCallbacks.push(()=>{ setTimeout(()=>{ try{ const result=onFulfilled(this.value); resolve(result); }catch(error){ reject(error); } }); }); this.onRejectedCallbacks.push(()=>{ setTimeout(()=>{ try{ const reason=onRejected(this.reason); reject(reason); }catch(error){ reject(error); } }); }); } }); } catch(onRejected){ return this.then(null,onRejected); } }
|
(2)手写任务调度器
支持按照指定时间间隔执行任务,以及暂停、恢复和取消任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| class TaskScheduler { constructor(){ this.tasks=new Map(); } addTask(taskName,callback,interval){ if(this.tasks.has(taskName)){ console.log(`This task has already exited`); return; } const task = { callback, interval, timer: null, isPaused: false, }; task.timer = setInterval(()=>{ if(!task.isPaused) { task.callback(); } },interval); this.tasks.set(taskName,task); console.log(`Task ${taskName} has been added`) ; } pauseTask(taskName) { const task=this.tasks.get(taskName); if(task){ task.isPaused=true; console.log(`Task ${taskName} is paused`); }else{ console.log(`Task ${taskName} doesn't exited`); } } resumeTask(taskName){ const task=this.tasks.get(taskName); if(task){ task.isPaused=false; console.log(`Task ${taskName} is unpaused`); }else{ console.log(`Task ${taskName} doesn't exited`); } } cancelTask(taskName){ const task=this.tasks.get(taskName); if(task){ clearInterval(task.timer); this.tasks.delete(taskName); console.log(`Task ${taskName} is canceled`); }else{ console.log(`Task ${taskName} doesn't exited`); } } }
|
(3)手写LRU
LRU缓存的特点是,当缓存达到容量限制时,最少被使用的缓存项会被移除。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class LRUCache { constructor(capacity){ this.capacity=capacity; this.cache=new Map(); } getCache(key) { if(!this.cache.has(key)){ return -1; } const value=this.cache.get(key); this.cache.delete(key); this.cache.set(key,value); return value; } put(key,value) { if(this.cache.has(key)){ this.cache.delete(key); }else if(this.cache.size>=this.capacity){ this.cache.delete(this.cache.keys().next().value); } this.cache.set(ke,value); } }
const lruCache = new LRUCache(2);
|
Map.keys()
this.cache.keys()
这个方法返回一个迭代器(Iterator),用于遍历 Map
中所有的键。这个迭代器是按插入顺序生成的;
next()
是迭代器的方法,用于获取迭代器的下一个值。调用 next()
方法会返回一个对象,包含两个属性:
- value:下一个键的值。
- done:一个布尔值,表示迭代器是否已完成。
通用性:任何 Map
对象都可以使用 .keys()
方法,无论其中存储的键是什么类型(字符串、数字、对象等)。
返回的迭代器:这个迭代器的行为是统一的,能够通过 next()
方法逐个访问键,并在遍历完成时返回一个 { done: true }
的对象。
(4)手写深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function deepClone(obj,hash=new WeakMap()) { if(obj===null||typeof obj!="object") return obj; if(obj instanceof Date) return new Date(obj); if(obj instanceof RegExp) return new RegExp(obj); if(hash.has(obj)) return hash.get(obj); const cloneObj=Array.isArray(obj)?[]:{}; hash.set(obj,cloneObj); for(let key in obj) { if(obj.hasOwnProperty(key)) { cloneObj[key]=deepClone(obj[key],hash); } } return cloneObj; }
|
hasOwnProperty
方法用于检查 obj
是否具有指定的属性 key
。这是为了确保只拷贝对象的自有属性(即直接在该对象上定义的属性),而不包括继承自原型链的属性。
- 例如,假设有一个对象
obj
,它继承了某些属性,但我们只想拷贝自己定义的属性,使用 hasOwnProperty
可以过滤掉那些继承的属性。
(5)手写new
步骤
- 创建一个新的对象:空对象
- 设置原型:将新对象的原型指向构造函数的原型
- 绑定this:将构造函数的this绑定到新创建的对象上
- 执行构造函数:调用构造函数,并将新对象作为上下文(this)
- 返回对象:如果构造函数返回了一个对象,则返回该对象;否则返回新创建的对象
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| function myNew(cunstructor,...args) { const obj={}; Object.setPrototypeOf(obj,constructor.prototype); const result=constructor.apply(obj,args); return (result&&typeof result==='object')?result:obj; }
function Person(name,age) { this.name=name; this.age=age; }
const alice=myNew(Person,"Alice",20);
console.log(alice.name); console.log(alice instanceof Person);
|
(6)写一个重复执行的函数
1、方法一:使用setInterval
1 2 3 4 5 6 7 8 9
| function repeatFunction() { console.log("函数被重复执行"); }
const intervalId=setInterval(repeatFunction,1000);
setTimeout(()=>{ clearInterval(intervalId); },5000);
|
2、setTimeout递归调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function repeatFunction(){ console.log("函数被重复执行"); setTimeout(repeatFunction,1000); }
repeatFunction();
let count=0; const maxCount=5;
function repeatWithLimit() { if(count<maxCount){ console.log(); count++; setTimeout(repeatWithLimit,1000); }else{ console.log(); } }
|
(7)写函数统计页面上所有的DOM元素,以对象的形式返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function countDOMElements() { const allElements=document.querySelectorAll('*'); const elementCount={}; allElements.forEach((element)=>{ const tagName=element.tagName.toLowerCase(); if(elementCount[tagName]){ elementCount[tagName]++; }else{ elementCount[tagName]=1; } }); }
|
(8)数组拍平
1 2 3 4 5 6 7 8 9 10 11
| function customFlat(arr,depth=1){ if(depth<1) return arr.slice(); return arr.reduce((acc,cur)=>{ if(Array.isArray(cur)){ return acc.concat(customFlat(cur,depth-1)); } else{ return acc.concat(cur); } },[]); }
|
(9)函数柯里化
函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function curry(fn,args=[]){ return function(){ let rest=[...args,...arguments]; if(rest.length<fn.length){ return curry.call(this,fn,rest); }else{ return fn.apply(this,rest); } } }
|
(10)sum(1)(2)(3)
1 2 3 4 5 6 7 8
| function sum(...args){ const total=args.reduce((acc,cur)=>acc+cur,0); const inner=(...newArgs)=>{ return sum(total+newArgs.reduce((acc,cur)=>acc+cur,0)); }; inner.value=()=>total; return inner; }
|
限定每次调用参数只有一个:
1 2 3 4 5 6 7
| function sum(num){ const inner=(nextNum)=>{ return sum(num+nextNum); }; inner.value=()=>num; return inner; }
|
(11)实现数组去重
1 2 3
| function uniq(arry){ return [...new Set(arry)]; }
|
1 2 3 4 5 6 7 8 9
| function uniq(arry){ let result=[]; for(let i=0;i<arry.length;i++){ if(!result.includes(arry[i])){ result.push(arry[i]); } } return result; }
|
(12)实现计时器
1 2 3 4 5 6 7 8
| class Timer={ constructor(){ this.time=0; } increase(){this.time+=1;} decrease(){this.time-=1;} getTime(){return time}; }
|
(13)蛇形打印树的节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class TreeNode{ constructor(value){ this.value=value; this.left=null; this.right=null; } }
function zigzagLevelOrder(root){ if(!root) return[]; const result=[]; const currentStack=[root]; const nextStack; let leftToRight=true; while(currentStack.length>0){ const levelValues=[]; while (currentStack.length>0){ const node=currentStack.pop(); levelValues.push(node.value); if(leftToRight){ if(node.left) nextStack.push(node.left); if(node.right) nextStack.push(node.right); }else{ if(node.right) nextStack.push(node.right); if(node.left) nextStack.push(node.left); } } result.push(levelValues); [currentStack,nextStack]=[nextStack,currentStack]; leftToRight=!leftToRight; } }
|
(14)链表拍平
1、迭代
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function flatten(head){ if(!head) return head; let cur=head; let stack=[]; while(cur){ if(cur.child){ if(cur.next) stack.push(cur.next); cur.next=cur.child; cur.child=null; } if(!cur.next&&stack.length>0){ cur.next=stack.pop(); } cur=cur.next; } return head; }
|