本篇文章用于总结自己在前端面试的过程中所遇到的手撕题和前端常考的算法题,其主要包括题目与Javascript实现的解法。一部分题目的代码由自己编写实现,还有一部分题目的代码来源互联网,所有代码都经过自己的运行验证,请放心食用。后续还会持续更新。。。
 
场景手写题 手写debounce 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function  debounce (fn,delay=100  ) {    let  timer = null ;     return  function ( ) {         if (timer){             clearTimeout (timer);         }         timer = setTimeout (()=> {             fn.apply(this ,arguments );             timer = null ;         },delay)     } } input.addEventListener('keyup' ,debounce(function ( ) {     console .log(input.value); },100 )) 
 
手写throttle 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function  throttle (fn,delay = 100  ) {    let  timer = null ;     return  function ( ) {         if (timer){             return  ;         }         timer = setTimeout (()=> {             fn.apply(this ,arguments );             timer = null ;         },delay)     } } div.addEventListener('drag' ,throttle(function (e ) {     console .log(e.offsetX,e.offsetY); },100 )) 
 
手写flat 1 2 3 4 5 6 7 8 9 10 11 12 13 function  flatDeep (arr,deep = 1  ) {    if (arr.length == 0 ) return  arr;     let  result = [];     for (let  i=0 ;i<arr.length;i++){         if (Array .isArray(arr[i])){          deep>0 ?(result = result.concat(flatDeep(arr[i],deep-1 ))):(result.push(arr[i]))         }else {             result.push(arr[i]);         }     }     return  result; } 
 
手写深拷贝 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 function  deepClone (obj = {} ) {    if (typeof  obj != 'object'  || obj == null ){         return  obj;     }     let  result;     if (obj instanceof  Array ){         result = [];     }else {         result = {};     }     for (let  key in  obj){         if (obj.hasOwnProperty(key)){             result[key] = deepClone(obj[key]);         }     }     return  result; } function  deepClone (obj = {} ) {    if (typeof  obj != 'object'  || obj == null ){         return  obj;     }     let  result;     if (obj instanceof  Array ){         result = [];     }else {         result = {};     }     for (let  key in  obj){         if (obj.hasOwnProperty(key)&&obj[key]!=obj){             result[key] = deepClone(obj[key]);         }else  if (obj[key]===obj){ 			result[key] = result; 		}     }     return  result; } 
 
手写call 1 2 3 4 5 6 7 8 9 10 Function .prototype.myCall = function (context ) {    context =(context === null  || context === undefined ) ? window  : context          context.fn = this           let  args = [...arguments].slice(1 )      let  result= context.fn(...args)     delete  context.fn      return  result } 
 
手写apply 1 2 3 4 5 6 7 Function .prototype.myApply = function (context ) {    context =(context === null  || context === undefined ) ? window  : context;     context.fn = this ;     let  result = arguments [1 ] ? context.fn(...arguments[1 ]) : context.fn()     delete  context.fn;     return  result; } 
 
手写bind 1 2 3 4 5 6 7 8 9 10 Function .prototype.myBind = function (context ) {    context =(context === null  || context === undefined ) ? window  : context     let  o = Object .create(context)     o.fn = this      let  args = [...arguments].slice(1 )     let  result= function ( ) {           	 o.fn(...args)     }     return  result } 
 
手写promise.all 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   Promise .all = function  (arr )  {          const  values = new  Array (arr.length)           let  resolvedCount = 0           return  new  Promise ((resolve, reject ) =>  {              arr.forEach((p, index ) =>  {         Promise .resolve(p).then(           value => {             resolvedCount++                                        values[index] = value                          if  (resolvedCount===promises.length) {               resolve(values)             }           },           reason => {              reject(reason)           }         )       })     })   } 
 
手写promise.race 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 Promise .myRace = function (arr ) {    return  new  Promise ((resolve,reject )=> {         arr.forEach((p,index )=> {             Promise .resolve(p).then(             	value=>{                     resolve(value)                 },                 reason=>{                     reject(reason);                 }             )         })     }) } let  p1 = new  Promise ((resolve, reject ) =>  {    resolve('ok1' ) }) let  p2 = new  Promise ((resolve, reject ) =>  {    setTimeout (() =>  {resolve('ok2' )}, 1000 ) }) let  p3 = new  Promise ((resolve, reject ) =>  {    resolve('ok3' ) }) Promise .myRace([p2,p1,p3]).then(result => {    console .log(result); }) 
 
手写promise.resolveDelay 1 2 3 4 5 6 7 8 9 10 11 12 13 Promise .resolveDelay = function  (value, time )  {     return  new  Promise ((resolve, reject ) =>  {          setTimeout (() =>  {              if  (value instanceof  Promise ) {                                 value.then(resolve, reject)                               } else  {                  resolve(value)              }          }, time);      })  } 
 
手写Promise.rejectDelay 1 2 3 4 5 6 7 Promise .rejectDelay = function  (reason, time )  {     return  new  Promise ((resolve, reject ) =>  {          setTimeout (() =>  {              reject(reason);         }, time)     })  } 
 
手写LazyMan函数 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 const  lazyman = function  (name )  {   console .log(`Hi,你好${name} ` );    return  {      executeChain: Promise .resolve(),      eat,      sleep    }  };  const  eat = function  (food )  {    this .executeChain = this .executeChain.then(() =>  new  Promise (resolve  =>  {      console .log(`eat ${food} ` );      resolve();    }))    return  this ;  }  const  sleep = function  (time )  {    this .executeChain = this .executeChain.then(() =>  new  Promise (resolve  =>  {      setTimeout (() =>  {        resolve();      }, time)    }))    return  this ;  } 
 
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 let  rest=function (_data=1000  ) {	return  Promise .race([ 		upload(), 		uploadTimeout(_data) 	]).then((value )=> { 		console .log(value) 	}) } function  upload ( ) {	console .log('请求进行中...' ); 	return  new  Promise ((resolve,reject )=> { 		let  xhr=new  XMLHttpRequest(); 		xhr.open('GET' ,"https://www.baidu.com" ); 		xhr.onload=function ( ) { 			if (xhr.readyState==4  && (xhr.status>=200  && xhr.status<300 )){ 				setTimeout (()=> { 					resolve("请求成功!" ) 				},2000 ) 			}else { 				reject(xhr.status) 			} 		} 		xhr.onerror=function ( ) { 			reject('请求失败了...' ) 		} 		xhr.send(null ); 	}) }; function  uploadTimeout (times ) {	return  new  Promise ((resolve,reject )=> { 		setTimeout (()=> { 			reject('请求超时,请重试' ); 		},times) 	}) } 
 
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 function  CancelablePromise ( )  {  this .pendingPromise = null ; } CancelablePromise.prototype.request = function  (requestFn )  {   if  (this .pendingPromise) {     this .cancel("取消重复请求" );   }   const  _promise = new  Promise ((resolve, reject ) =>  (this .reject = reject));   this .pendingPromise = Promise .race([requestFn(), _promise]);   return  this .pendingPromise; }; CancelablePromise.prototype.cancel = function  (reason )  {   this .reject(new  Error (reason));   this .pendingPromise = null ; }; function  createRequest (delay )  {  return  () =>      new  Promise ((resolve ) =>  {       setTimeout (() =>  {         resolve("done" );       }, delay);     }); } const  cancelPromise = new  CancelablePromise();for  (let  i = 0 ; i < 5 ; i++) {  cancelPromise     .request(createRequest(1000 ))     .then((res ) =>  console .log(res))      .catch((err ) =>  console .error(err));  } setTimeout (() =>  {     cancelPromise     .request(createRequest(1000 ))     .then((res ) =>  console .log(res))     .catch((err ) =>  console .error(err));    cancelPromise.cancel("手动取消" ); }, 3000 ); setTimeout (() =>  {  cancelPromise     .request(createRequest(1000 ))     .then((res ) =>  console .log(res))      .catch((err ) =>  console .error(err)); }, 4000 ); 
 
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 function  concurrentRequest (requestFns, limit )  {     function  recursion (requestFn )  {     requestFn().finally(() =>  {       if  (_requestFns.length > 0 ) {         recursion(_requestFns.pop());       }     });   }   const  _requestFns = [...requestFns];      for  (let  i = 0 ; i < limit && _requestFns.length > 0 ; i++) {     recursion(_requestFns.pop());   } } function  createRequest (delay )  {  return  () =>      new  Promise ((resolve ) =>  {       setTimeout (() =>  {         resolve("done" );       }, delay);     }).then((res ) =>  {       console .log(res);     }); } const  requestFns = [];for  (let  i = 0 ; i < 10 ; i++) {  requestFns.push(createRequest(1000 )); } concurrentRequest(requestFns, 3 ); 
 
Promise全局loading态 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 function  PromiseManager ( )  {  this .pendingPromise = new  Set ();   this .loading = false ; } PromiseManager.prototype.generateKey = function  ( )  {   return  `${new  Date ().getTime()} -${parseInt (Math .random() * 1000 )} ` ; }; PromiseManager.prototype.push = function  (...requestFns )  {   for  (const  requestFn of  requestFns) {     const  key = this .generateKey();     this .pendingPromise.add(key);     requestFn().finally(() =>  {       this .pendingPromise.delete(key);       this .loading = this .pendingPromise.size !== 0 ;     });   } }; function  createRequest (delay )  {  return  () =>      new  Promise ((resolve ) =>  {       setTimeout (() =>  {         resolve("done" );       }, delay);     }).then((res ) =>  console .log(res)); } const  manager = new  PromiseManager();manager.push(createRequest(1000 )); manager.push(createRequest(4500 )); const  id = setInterval (() =>  {  console .log(manager.loading);   if  (!manager.loading) clearInterval (id); }, 1000 ); 
 
手写柯里化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function  Curry (fn ) {    var  arr = [];     return  function ( ) {         if (arguments .length>0 ){             arr = arr.concat(Array .from(arguments ));             return  arguments .callee;         }else {             return  fn.apply(null ,arr);         }     } } let  fn = Curry(function ( ) {    let  arr = Array .from(arguments );     let  sum = arr.reduce(function (total,item ) {         return  total+item;     })     return  sum; }) var  s= fn(1 )(2 )()console .log(s)
 
两栏布局 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 <div class  ="left" >left</div> <div class  ="right" >right</div> body,div{     padding: 0 ;     margin:0 ; } .left,.right{     height: 200px; } .left{     float: left;     width: 200px;     background-color:skyblue; } .right{     margin-left: 200px;     background-color: greenyellow; } <div class  ="left" >left</div> <div class  ="right" >right</div> body,div{     padding: 0 ;     margin:0 ; } .left,.right{     height:200px; } .left{     float:left;     width: 200px;     background-color:skyblue; } .right{     overflow:hidden;     background-color: greenyellow; } <div class  ="wrap" >     <div class  ="left" >left</div> 	<div class  ="right" >right</div> </div> body,div{     padding: 0 ;     margin:0 ; } .wrap{     display: flex; } .left,.right{     height: 200px; } .left{     width: 200px;     background-color:skyblue; } .right{     flex: 1 ;      background-color: greenyellow; } 	flex-grow : 1 ;  	flex-shrink : 1 ;  	flex-basis : 0 ;  
 
双飞翼布局(三栏布局) 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 <style> #inside {     margin: 0  200px 0  180px;     height: 100px; } #center {     float: left;     width: 100 %;          height: 100px;     background: blue; } #left {     float: left;     width: 180px;     height: 100px;     margin-left: -100 %;     background: #0c9; } #right {     float: left;     width: 200px;     height: 100px;     margin-left: -200px;     background: #0c9; } </style> <body>     <div id="center" >         <div id="inside" >middle</div>     </div>     <div id="left" >left</div>     <div id="right" >right</div> </body> 
 
圣杯布局(三栏布局) 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 <style> #bd{     padding: 0  200px 0  180px;     height: 100px; } #middle{     float: left;     width: 100 %;     height: 500px;     background:blue; } #left{     float:left;     width:180px;     height:500px;     margin-left:-100 %;     background: #0c9;     position: relative;     left: -180px; } #right{     float: left;     width: 200px;     height: 500px;     margin-left: -200px;     background: #0c9;     position: relative;     right: -200px; } </style> <body>     <div id="bd" >         <div id="middle" >middle</div>         <div id="left" >left</div>         <div id="right" >right</div>     </div> </body> 
 
排序及查找算法 快速排序O(nlogn) 不稳定的排序
思想:划定两个区域,-1位置表示小于num的区域,数组长度+1的位置表示大于num的区域,然后cur指针从0位置开始遍历。如果小于num,把小于区域的下一个位置 和cur位置交换,交换完之后小于等于区域扩大一个位置cur也++。如果等于num,cur直接+1其他不变。如果大于num,把大于区域的前一个位置和cur交换,more前移动一个位置,cur不变。
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 function  quickSort (arr,L=0 ,R=arr.length-1  ) {	if (L<R){ 		let  p = partition(arr,L,R); 		quickSort(arr,L,p[0 ]); 		quickSort(arr,p[1 ],R); 	} 	function  partition (arr,L,R ) { 		let  less = L-1 ,more = R+1 ,cur = L; 		let  num = arr[R]; 		while (cur<more){ 			if (arr[cur]<num){ 				swap(arr,++less,cur++); 			}else  if (arr[cur]>num){ 				swap(arr,cur,--more); 			}else { 				cur++; 			} 		} 		return  [less,more]; 	} 	function  swap (arr,i,j ) { 		let  temp = arr[i]; 		arr[i] = arr[j]; 		arr[j] = temp; 	} 	return  arr; } 
 
归并排序O(nlogn) 可以实现稳定的排序
思想:使用递归先对数组进行无限分割,然后再对分割出来的两块进行合并;
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 function  mergeSort (arr,l,r ) {    if (l>=r) return  0 ;     let  mid = Math .floor((l+r)/2 );     mergeSort(arr,l,mid);     mergeSort(arr,mid+1 ,r);     let  temp=[];     let  i = l;     let  j = mid+1 ;     while (i<=mid && j<=r){         if (arr[i]<=arr[j]){             temp.push(arr[i]);             i++;         }else {             temp.push(arr[j]);             j++;         }     }     while (i<=mid){         temp.push(arr[i]);         i++;     }     while (j<=r){         temp.push(arr[j]);         j++;     }     for (let  i=l,j=0 ;i<=r;i++,j++){         arr[i] = temp[j];     } } 
 
堆排序O(nlogn) 不稳定的排序
思想:
建立大根堆:对于每个小二叉树,通过其子节点的(n-1)/2找到其父节点的位置,当父节点小于子节点时交换位置,依次循环。 
然后将堆顶元素和树的最后一个叶子节点进行交换,此时最后一个叶子节点上的值为当前树中的最大值,然后将除开此叶子节点的树重复1-2的过程。 
 
冒泡排序O(n^2) 可以实现稳定的排序
1 2 3 4 5 6 7 8 9 10 11 12 13 function  bubble (arr ) {    if (arr.length === 0  ) return  arr;     for (let  i=arr.length-1 ;i>=0 ;i--){         for (let  j=0 ;j<i;j++){             if (arr[j]>arr[j+1 ]){                 let  temp = arr[j];                 arr[j] = arr[j+1 ];                 arr[j+1 ] = temp;             }         }     }     return  arr; } 
 
插入排序O(n^2) 可以实现稳定的排序
1 2 3 4 5 6 7 8 9 10 11 12 13 function  insertSort (arr ) {    if (arr.length===0 ) return  arr;     for (let  i=1 ;i<arr.length;i++){         for (let  j=i;j>0 ;j--){             if (arr[j]<arr[j-1 ]){                 let  temp = arr[j];                 arr[j] = arr[j-1 ];                 arr[j-1 ] = temp;             }         }     }     return  arr; } 
 
选择排序O(n^2) 不稳定的排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function  selectSort (arr ) {    if (arr.length===0 ) return  arr;     let  min,temp;     for (let  i=0 ;i<arr.length;i++){         min=i;         for (let  j=i;j<arr.length;j++){             if (arr[min]>arr[j]){               	min =j;               }         }         temp = arr[i];         arr[i] = arr[min];         arr[min] = temp;     }     return  arr; } 
 
二分查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function  search (arr,data ) {    var  max = arr.length-1 ,          min = 0 ;       while (min<=max){         var  mid = Math .floor((max+min)/2 );          if (arr[mid]<data){             min = mid+1 ;         }else  if (arr[mid]>data){             max = mid-1 ;         }else {             return  mid;          }     }     return  false ;    }     
 
树(Tree) 先序遍历(递归) 1 2 3 4 5 6 7 8 9 10 11 12 var  preorderTraversal = function (root )  {    if (root===null ) return  [];     let  res=[];     function  dfs (root ) {         if (!root) return  null ;         res.push(root.val);         dfs(root.left);         dfs(root.right);     }     dfs(root);     return  res; }; 
 
先序遍历(非递归) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var  preorderTraversal = function (root )  {    let  res = [];     let  stack = [];     while (root || stack.length!=0 ){         while (root){             res.push(root.val);             stack.push(root);             root = root.left;         }         root = stack.pop();         root = root.right;     }     return  res; }; 
 
中序遍历(递归) 按先序类推
中序遍历(非递归) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var  inorderTraversal = function (root )  {    let  res = [];     let  stack = [];     while (root || stack.length){         while (root){             stack.push(root);             root = root.left;         }         root = stack.pop();         res.push(root.val);         root = root.right;     }     return  res; }; 
 
后续遍历(递归) 按先序类推
后续遍历(非递归) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var  postorderTraversal = function (root )  {    let  res = [];     let  stack = [];     while (root || stack.length){         while (root){             stack.push(root);             res.unshift(root.val);             root = root.right;         }         root = stack.pop();         root = root.left;     }     return  res; }; 
 
二叉树的层序遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var  levelOrder = function (root )  {    if (!root) return  [];     let  res = [],stack=[root];     let  i = 0      while (stack.length){         let  len = stack.length;         res[i] = [];         while (len){             let  cur = stack.shift();             res[i].push(cur.val)             if (cur.left) stack.push(cur.left);             if (cur.right) stack.push(cur.right);             len--;         }         i++;     }     return  res; }; 
 
其他 js实现大数相加 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 function  addString (a,b ) {    if (a.length<b.length){         a = a.padStart(b.length,"0" )       }     if (a.length>b.length){         b = b.padStart(a.length,"0" );     }     let  addOne = 0 ;     let  res = [];     for (let  i=a.length-1 ;i>=0 ;i--){         let  sum = Number (a[i])+Number (b[i])+addOne;         if (sum>9 ){             res.unshift(sum-10 );             addOne = 1 ;         }else {             res.unshift(sum);             addOne = 0 ;         }     }     if (addOne){         res.unshift(addOne);     }     return  res.join("" ) } 
 
判断表达式是否闭合 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 function  fn (arr ) {    if (arr.length === 0 ) return  false ;     let  stack = [];     while (arr.length>0 ){         let  c = arr.shift();         if (c === "("  || c==="["  || c=== "{" ){             stack.push(c);         }else  if (c===")"  || c==="]"  || c=== "}" ){             if (test(stack.pop()) != c){                 return  false ;             }         }     }     function  test (str ) { 		if (str === "(" ) return  ")" ;         if (str === "[" ) return  "]" ;         if (str === "{" ) return  "}" ;     }     if (stack.length ===0 ){             return  true ;     }else {         return  false ;     } }