math.h|手撕前端面试代码题大全

关注 程序员成长指北,回复“1”

加入我们一起学习,天天进步
math.h|手撕前端面试代码题大全
文章图片

转载自:等不到你. https://blog.csdn.net/weixin_43758603/article/details/109895826
写在前面
  1. 最近我终于找到了一份满意的工作.准备面试的过程中,我整理出了一些有用的笔记,这篇就是其中之一.
    既然写好了,不妨就放在这里分享给大家.
  2. 面试通常都有现场写代码的题目,我基本每次都或多或少的翻车.有意思的是,每次面试结束,自己改不到五分钟就调试出来了.
  3. 所以面试中的写代码的过程,一定不能紧张,要沉住气慢慢来.只要不是系统自动检查结果,只要是面试官看着你写,就有很大的表现的机会,哪怕最后做不出来.
    我参加的最烦人的面试,是那种系统判定结果的面试,只要做不出来,就绝对不可能通过.
  4. 仔细想想,在二三十场面试中,很少有我完整写出毫无瑕疵的答案的题目,但基本也都顺利通过了.
    对比来看,我的同学中,答案能运行成功但面试没通过的也大有人在.
    可以肯定的是,面试官不会只根据是否能运行成功来评价应聘者.
    所以,只需要顺着正确思路稳稳地做就好了,不要怕最后运行不成功.
  5. 如果实在做不出,也一定要和面试官说清你当前的进展和思路,而不是一句"我不会"就想结束问题.
  6. 当然,我指的只是前端这个对算法能力要求不强的岗位…
    有一场百度的面试我甚至直接和面试官说,我是个偏感性的人,喜欢但是不擅长做算法.都给我通过了.
  7. 总的来看,如果能完全掌握这篇文章的内容,就足以应付所有前端面试中的手撕代码环节了.
  8. 来都来了,点个赞呗
导读
关于这篇文章,有几点我想先说清楚,方便读者更顺利的学习.
  • 这篇文章不适合前端小白阅读,需要对JSES6有一定了解,否则遇到一些写法可能不太看得懂
  • 因为精力有限,我只加了较为粗略但足以帮助读者理解的注释,因为多数题也只有几行代码而已.
    如果遇到还不懂的地方,我认为读者完全可以自己去查询文档来了解为什么这么做,为什么使用这个函数.
    或者,先查询该问题通常的解决思路,再回来参考我的实现
  • 代码大量使用了ES6的语法
  • 学习手撕代码,不只是理解的过程,更是实践的过程
    【math.h|手撕前端面试代码题大全】我在完全掌握(可以默写出每段代码,并讲清楚每一行的作用)以下代码的过程中,做了以下几件事
    • 参考别人的实现,结合自己的思路,写出一个自己的版本
    • 不断对代码进行优化
      当你尝试去优化一段代码的时候,对它的理解和记忆会异常深刻
    • 不看之前的实现,重新自己实现一次
      再和之前的实现做对比,检查错误
    • 反复阅读和默写,直到可以完全正确的默写为之
  • 作为一个专业的程序员,除了工作中的编码,额外的无实际产出的练习(反复练习解决一个问题,反复默写同一段代码),也是必不可少的.
    这就像歌手不可能到了舞台上才去练习自己的声音.他一定会在平时大量去练声.
    这就是我强调要反复敲代码的原因.别想着平时只要理解,工作中再去熟能生巧.
    工作不是给你练习的地方,工作是你的舞台.
  • 下文中几乎每一段代码,都是我反复优化后的结果,希望可以带给读者新的启发.
  • 我把代码大致分成了几个专题,一共包含了大致30个问题的解决方案
  • 除了文章中的问题,还有些我没有提到的,都是频率较低的问题
    关于算法题,除了排序和查找我也基本没有写.因为算法问题千变万化,需要的是解决问题的思维,而不是固定的实现
  • 重要性与顺序无关
  • 有问题可以问我,我都会回复
目录
DOM
  • 事件代理
数组 对象
  • 扁平化
  • 去重 - unique()
  • 拷贝
    • 浅拷贝
    • 深拷贝(copy()函数实现、JSON.stringify
字符串
  • 去除空格 - trim()
  • 字符串全排列
    • 广度优先实现
    • 深度优先实现
排序和查找
  • 插入排序
  • 归并排序
  • 快速排序
  • 二分查找
  • 找出出现次数最多的元素 - getMostItem()
功能函数实现
  • setTimeout实现setInterval
  • 函数柯里化
  • 防抖 节流
数据结构
  • 单链表
设计模式
  • 发布订阅模式
JS原生API实现
  • bind() call() apply()
  • InstanceOf
  • new
  • reduce() forEach()
  • Promise
HTTP请求
  • AJAX封装
  • JSONP
DOM 事件代理
document.getElementById("father-id").onclick=function(event){ event=event||window.event let target=event.target||event.srcElement //可以自己打印一下event.target.nodeName,看看是什么 if (target.nodeName.toLowerCase()==='xxx'){ //事件内容 } }

数组 对象 扁平化
function flatten(arr) { let result=[] for (let i=0,len=arr.length; i

去重 - unique()
function unique(arr) { let appeard=new Set() return arr.filter(item=>{ //创建一个可以唯一标识对象的字符串id let id=item+JSON.stringify(item) if (appeard.has(id)) { return false } else { appeard.add(id) return true } }) }

拷贝
浅拷贝
function copy(obj) { let result=Array.isArray(obj)?[]:{} Object.keys(obj).forEach(key=>result[key]=obj[key]) return result }

otherStar={...star}

Object.assign({},star)

深拷贝 copy()函数实现 处理了循环引用和key为symbol类型的情况
function copy(obj,appeard=new Map()) { if (!(obj instanceof Object)) return obj//如果是原始数据类型 if (appeard.has(obj)) return appeard.get(obj)//如果已经出现过let result=Array.isArray(obj)?[]:{} appeard.set(obj,result)//将新对象放入map//遍历所有属性进行递归拷贝 ; [...Object.keys(obj),...Object.getOwnPropertySymbols(obj)] .forEach(key=>result[key]=copy(obj[key],appeard))return result }

JSON.stringify
  • 只能处理纯JSON数据
  • 有几种情况会发生错误
  • 包含不能转成 JSON 格式的数据
  • 循环引用
  • undefined,NaN, -Infinity, Infinity 都会被转化成null
  • RegExp/函数不会拷贝
  • new Date()会被转成字符串
new=JSON.parse(JSON.stringify(old))

字符串 去除空格 - trim()
function myTrim(str) { return str.replace(/(^\s+)|(\s+$)/g,'')//将前空格和后空格替换为空 }

function myTrim(str) {//记录前后空格的个数,最后对字符串进行截取 let first=0,last=str.length for (let i in str) { if (str[i]===' ') { first++ } else { break } } for (let i=last; i>first; i--) { if (str[i]===' ') { last-- } else { break } } return str.substr(first,last-first) }

字符串全排列
广度优先实现
function combine(str) {//抽出一个字符s,对其余的进行排列,将s放在每种排列开头 if (str.length===1) return [str] let results=[] for (let i in str) { for (let s of combine(str.slice(0,i)+str.slice(1+(+i)))) { results.push(str[i]+s) } } //可能会出现类似"aa"=>[aa,aa,aa,aa]的情况,需要去重 return [...new Set(results)] }

深度优先实现
function combine(str) {//记录已经使用过的字符,深度优先访问所有方案 let result=[] ; (function _combine(str,path=''){ if (str.length===0) return result.push(path) for (let i in str) { _combine(str.slice(0,i)+str.slice((+i)+1,str.length),path+str[i]) } })(str) //可能会出现类似"aa"=>[aa,aa,aa,aa]的情况,需要去重 return [...new Set(result)] }

排序和查找 插入排序
function sort(arr) {//原地 for (let i in arr) {//选一个元素 while (i>0&&arr[i]

归并排序
function sort(arr) { if (arr.length===1) return arr //分成两部分 let mid=Math.floor(arr.length/2) let [part1,part2]=[sort(arr.slice(0,mid)),sort(arr.slice(mid))] //对比+合并 let result=[] while (part1.length>0&&part2.length>0) result.push((part1[0]

快速排序
function sort(arr) { if (arr.length<=1) return arr//选基准值 let mid_pos=arr.length>>1 let mid=arr.splice(mid_pos,1)[0] let left=[],right=[]//和基准值比较,分别插入left,right数组 arr.forEach(item=>(item<=mid?left:right).push(item)) return [...sort(left),mid,...sort(right)]//递归调用排序 }

二分查找
function search(arr,target) {//循环写法,不断移动左右指针,缩小范围 let left=0,right=arr.length-1 while (left<=right) { const mid_pos=Math.floor((left+right)/2) const mid_val=arr[mid_pos]if (target===mid_val) { return mid_pos } else if (target>mid_val) { left=mid_pos+1 } else { right=mid_pos-1 } } return -1 }

找出出现次数最多的元素 - getMostItem()
function getMost(arr) { //计数 let map=new Map() arr.forEach(item=>{ if (map.has(item)) { map.set(item,map.get(item)+1) } else { map.set(item,1) } }) //找出出现最多 let [max_vals,max_num]=[[arr[0]],map.get(arr[0])] map.forEach((count,item)=>{ if (count>max_num){ max_vals=[item] max_num=count } else { max_vals.push(item) } }) return max_vals }console.log(getMost(['1', '2', '3', '3', '55', '3', '55', '55']))

功能函数实现 setTimeout实现setInterval
function myInterval(fn,interval,...args) { let context=this setTimeout(()=>{ fn.apply(context,args) myInterval(fn,interval,...args)//别忘了为它传入参数 },interval) }myInterval((num)=>console.log(num),500,10)

函数柯里化
function sum(...args1){ return function (...args2) { return [...args1,...args2].reduce((p,n)=>p+n) } } console.log(sum(1, 2, 2)(7))

防抖 节流
实现了两个加工方法,返回一个加工后的防抖/节流函数
防抖
function debounce(fn,delay) { let timer=null return function (){ if (timer) clearTimeout(timer) timer=setTimeout(()=>fn.call(...arguments),delay)//别忘了为它传入参数 } }

节流
function throttle(fn,delay) { let flag=true return function() { if (!flag) returnflag=false setTimeout(()=>{ fn(...arguments)//别忘了为它传入参数 flag=true },delay) } }

数据结构 单链表
function Node(element) {//结点类 [this.element,this.next]=[element,null] }class LinkList {//链表类 constructor() { this.length=0 this.head=new Node() this.tail=new Node() this.head.next=this.tail } get_all() { let result=[] let now=this.head while (now.next!==this.tail) { now=now.next result.push(now.element) } return result } unshift(element) {//开头添加 let node=new Node(element) node.next=this.head.next this.head.next=node } shift(){//开头删除 let node=this.head.next this.head.next=this.head.next.next return node.element } } let list=new LinkList() list.unshift(15) list.unshift(16) list.unshift(17) console.log(list.shift())//17 console.log(list.get_all())//[ 16, 15 ]

设计模式 发布订阅模式
class Observer { constructor() { this.events={}//事件中心 } publish(eventName,...args) {//发布=>调用事件中心中对应的函数 if (this.events[eventName]) this.events[eventName].forEach(cb=>cb.apply(this,args)) } subscribe(eventName,callback) {//订阅=>向事件中心中添加事件 if (this.events[eventName]) { this.events[eventName].push(callback) } else { this.events[eventName]=[callback] } } unSubscribe(eventName,callback) {//取消订阅 if (events[eventName]) events[eventName]=events[eventName].filter(cb=>cb!==callback) } }

JS原生API实现 bind() call() apply()
apply()
Function.prototype.myApply=function(context,args) { context.fn=this//为context设置函数属性 let result=context.fn(...args)//调用函数 delete context.fn//删除context的函数属性 return result }

call()
//除了...args //和apply都一样 Function.prototype.myCall=function(context,...args) { context.fn=this let result=context.fn(...args) delete context.fn return result }

bind()
Function.prototype.myBind=function(context,args1) {//使用[闭包+apply]实现 return (...args2)=>this.apply(context,[...args1,...args2]); }

InstanceOf
function myInstanceOf(son,father) {//沿着父亲的原型链向上查找是否有儿子的原型 while (true) { son=son.__proto__ if (!son) return false if (son===father.prototype) return true } }myInstanceOf([], Array)// true

new
function myNew(constructor_fn,...args) { //构造新的空对象 let new_obj={} new_obj.__proto__=constructor_fn.prototype let result=constructor_fn.apply(new_obj,args) //如果构造函数没有返回一个对象,则返回新创建的对象 //如果构造函数返回了一个对象,则返回那个对象 //如果构造函数返回原始值,则当作没有返回对象 return result instanceof Object?result:new_obj }function Animal(name) { this.name = name; }let animal = myNew(Animal, 'dog'); console.log(animal.name)// dog

reduce() forEach()
reduce() api用法:
arr.reduce(function(prev, cur, index, arr){}, initialValue)

实现:
Array.prototype.myReduce=function(fn,init_val){ let [val,idx]=init_val?[init_val,0]:[this[0],1]//设置初始值 for (let i=idx,len=this.length; ipre+item,0)) // 15

forEach() api用法:
[1,3,5,7,9].myForEach(function(item,index,arr) { console.log(this) },15)

实现:
Array.prototype.myForEach=function(fn,temp_this) { for (let i=0,len=this.length; i

Promise
Promise.all()
Promise.prototype.all=function(promiseList) { return new Promise((resolve,reject)=>{ if (promiseList.length===0) return resolve([]) let result=[],count=0promiseList.forEach((promise,index)=>{ Promise.resolve(promise).then(value=>{ result[index]=value if (++count===promiseList.length) resolve(result) },reason=>reject(reason)) }) }) }

ES6所有API完整实现 通过Promise/A+ test测试
实现细节过多,还请参照Promise/A+规范阅读
也可以直接参考我关于promise的笔记
深入理解promise
https://blog.csdn.net/weixin_43758603/article/details/109641486
class Promise { constructor(task) { this.status="pending" this.value=https://www.it610.com/article/undefined this.reason=undefined this.fulfilled_callbacks=[] this.rejected_callbacks=[]try { task(this._resolve,this._reject) } catch (error) { this._reject(error) } } then(onFulfilled,onRejected){ if (this.status==='fulfilled') { let promise2=new Promise((resolve,reject)=>{ setTimeout(()=>{ try { if (!this._isFunction(onFulfilled)) { resolve(this.value) } else { this._resolvePromise(promise2,onFulfilled(this.value)) } } catch (error) { reject(error) } },0) }) return promise2 } else if (this.status==='rejected') { let promise2=new Promise((resolve,reject)=>{ setTimeout(()=>{ try { if (!this._isFunction(onRejected)) { reject(this.reason) } else { this._resolvePromise(promise2,onRejected(this.reason)) } } catch (error) { reject(error) } },0) }) return promise2 } else if (this.status==='pending'){ let promise2=new Promise((resolve,reject)=>{ this.fulfilled_callbacks.push(()=>{ try { if (!this._isFunction(onFulfilled)) { resolve(this.value) } else { this._resolvePromise(promise2,onFulfilled(this.value)) } } catch (error) { reject(error) } }) this.rejected_callbacks.push(()=>{ try { if (!this._isFunction(onRejected)) { reject(this.reason) } else { this._resolvePromise(promise2,onRejected(this.reason)) } } catch (error) { reject(error) } }) }) return promise2 } } catch=onRejected=>this.then(null,onRejected) finally=onFinished=>this.then(onFinished,onFinished) static deferred(){ let deferred={} deferred.promise=new Promise((resolve,reject)=>{ deferred.resolve=resolve deferred.reject=reject }) return deferred } static resolve(value) { if (value instanceof Promise) return value return new Promise(resolve=>resolve(value)) } static reject=reason=>{return new Promise((resolve, reject)=>reject(reason))} static all(promiseList) { return new Promise((resolve,reject)=>{ if (promiseList.length===0) return resolve([]) let result=[],count=0promiseList.forEach((promise,index)=>{ Promise.resolve(promise).then(value=>{ result[index]=value if (++count===promiseList.length) resolve(result) },reason=>reject(reason)) }) }) } static race(promiseList) { return new Promise((resolve,reject)=>{ if (promiseList.length===0) return resolve() promiseList.forEach(promise=>{ Promise.resolve(promise) .then(value=>resolve(value),reason=>reject(reason)) }) }) } static allSettled(promiseList) { return new Promise(resolve=>{ let result=[],count=0 if (len===0) return resolve(result)promiseList.forEach((promise,i)=>{ Promise.resolve(promise).then(value=>{ result[i]={ status:'fulfilled', value:value } if (++count===promiseList.length) resolve(result) },reason=>{ result[i]={ status:'rejected', reason:reason } if (++count===promiseList.length) resolve(result) }) }) }) } _resolve=value=>{ if (this.status!=='pending') return setTimeout(()=>{ this.status ='fulfilled' this.value = https://www.it610.com/article/value this.fulfilled_callbacks.forEach(cb=>cb(this.value)) },0) } _reject=reason=>{ if (this.status!=='pending') return setTimeout(()=>{ this.reason = reason this.status ='rejected' this.rejected_callbacks.forEach(cb=>cb(this.reason)) },0) } _isFunction=f=>Object.prototype.toString.call(f).toLocaleLowerCase()==='[object function]' _isObject=o=>Object.prototype.toString.call(o).toLocaleLowerCase()==='[object object]' _resolvePromise(promise,x){ if (promise===x) { promise._reject(new TypeError('cant be the same')) return } if (x instanceof Promise) { if (x.status==='fulfilled') { promise._resolve(x.value) } else if (x.status==='rejected') { promise._reject(x.reason) } else if (x.status==='pending') { x.then(value=>{ this._resolvePromise(promise,value) },reason=>{ promise._reject(reason) }) } return } if (this._isObject(x)||this._isFunction(x)) { let then try { then=x.then } catch (error) { promise._reject(error) return } if (this._isFunction(then)) { let called=false try { then.call(x,value=>{ if (called) return called=true this._resolvePromise(promise,value) },reason=>{ if (called) return called=true promise._reject(reason) }) } catch (error) { if (called) return promise._reject(error) } } else { promise._resolve(x) } } else { promise._resolve(x) } } } module.exports = Promise

HTTP请求 AJAX封装
function ajax(method,url,params,callback) { //对参数进行处理 method=method.toUpperCase() let post_params=null let get_params='' if (method==='GET') { if (typeof params==='object') { let tempArr=[] for (let key in params) { tempArr.push(`${key}=${params[key]}`) } params=tempArr.join('&') } get_params=`?${params}` } else { post_params=params } //发请求 let xhr=new XMLHttpRequest() xhr.onreadystatechange=function(){ if (xhr.readyState!==4) return callback(xhr.responseText) } xhr.open(method,url+get_params,false) if (method==='POST') xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded') xhr.send(post_params) }ajax('get','https://www.baidu.com',{id:15},data=>console.log(data))

JSONP
function jsonp(url, params_obj, callback) { //创建一个供后端返回数据调用的函数名 let funcName = 'jsonp_' + Data.now() + Math.random().toString().substr(2, 5) //将参数拼接成字符串 if (typeof params==='object') { let temp=[] for (let key in params) { temp.push(`${key}=${params[key]}`) } params=temp.join('&') } //在html中插入

后端返回的
??爱心三连击1.看到这里了就点个在看支持下吧,你的「点赞,在看」是我创作的动力。 2.关注公众号程序员成长指北,回复「1」加入Node进阶交流群!「在这里有好多 Node 开发者,会讨论 Node 知识,互相学习」! 3.也可添加微信【ikoala520】,一起成长。“在看转发”是最大的支持

    推荐阅读