手撕前端面试代码题

本文作者在面试过程中整理出了一系列前端面试中常见的手撕代码题目,包括DOM操作、数组与对象处理、字符串操作、排序与查找算法、功能函数实现、数据结构、设计模式和HTTP请求等内容。通过反复优化和实践,这些代码涵盖了前端面试中的关键知识点,旨在帮助读者更好地准备面试。
摘要由CSDN通过智能技术生成

写在前面

  1. 最近我终于找到了一份满意的工作.准备面试的过程中,我整理出了一些有用的笔记,这篇就是其中之一.

    既然写好了,不妨就放在这里分享给大家.

  2. 面试通常都有现场写代码的题目,我基本每次都或多或少的翻车.有意思的是,每次面试结束,自己改不到五分钟就调试出来了.

  3. 所以面试中的写代码的过程,一定不能紧张,要沉住气慢慢来.只要不是系统自动检查结果,只要是面试官看着你写,就有很大的表现的机会,哪怕最后做不出来.

    我参加的最烦人的面试,是那种系统判定结果的面试,只要做不出来,就绝对不可能通过.

  4. 仔细想想,在二三十场面试中,很少有我完整写出毫无瑕疵的答案的题目,但基本也都顺利通过了.

    对比来看,我的同学中,答案能运行成功但面试没通过的也大有人在.

    可以肯定的是,面试官不会只根据是否能运行成功来评价应聘者.

    所以,只需要顺着正确思路稳稳地做就好了,不要怕最后运行不成功.

  5. 如果实在做不出,也一定要和面试官说清你当前的进展和思路,而不是一句"我不会"就想结束问题.

  6. 当然,我指的只是前端这个对算法能力要求不强的岗位…

    有一场百度的面试我甚至直接和面试官说,我是个偏感性的人,喜欢但是不擅长做算法.都给我通过了.

  7. 总的来看,如果能完全掌握这篇文章的内容,就足以应付所有前端面试中的手撕代码环节了.

  8. 来都来了,点个赞呗

  9. 转载请注明出处

  10. 有问题请直接且具体地指出,不要光说xxx有问题

导读

关于这篇文章,有几点我想先说清楚,方便读者更顺利的学习.

  • 这篇文章不适合前端小白阅读,需要对JSES6有一定了解,否则遇到一些写法可能不太看得懂

  • 因为精力有限,我只加了较为粗略但足以帮助读者理解的注释,因为多数题也只有几行代码而已.

    如果遇到还不懂的地方,我认为读者完全可以自己去查询文档来了解为什么这么做,为什么使用这个函数.

    或者,先查询该问题通常的解决思路,再回来参考我的实现

  • 代码大量使用了ES6的语法

  • 学习手撕代码,不只是理解的过程,更是实践的过程

    我在完全掌握(可以默写出每段代码,并讲清楚每一行的作用)以下代码的过程中,做了以下几件事

    • 参考别人的实现,结合自己的思路,写出一个自己的版本

    • 不断对代码进行优化

      当你尝试去优化一段代码的时候,对它的理解和记忆会异常深刻

    • 不看之前的实现,重新自己实现一次

      再和之前的实现做对比,检查错误

    • 反复阅读和默写,直到可以完全正确的默写为之

  • 作为一个专业的程序员,除了工作中的编码,额外的无实际产出的练习(反复练习解决一个问题,反复默写同一段代码),也是必不可少的.

    这就像歌手不可能到了舞台上才去练习自己的声音.他一定会在平时大量去练声.

    这就是我强调要反复敲代码的原因.别想着平时只要理解,工作中再去熟能生巧.

    工作不是给你练习的地方,工作是你的舞台.

  • 下文中几乎每一段代码,都是我反复优化后的结果,希望可以带给读者新的启发.

  • 我把代码大致分成了几个专题,一共包含了大致30个问题的解决方案

  • 除了文章中的问题,还有些我没有提到的,都是频率较低的问题

    关于算法题,除了排序和查找我也基本没有写.因为算法问题千变万化,需要的是解决问题的思维,而不是固定的实现

  • 重要性与顺序无关

  • 有问题可以问我,我都会回复

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<len;i++) {
   
		if (Array.isArray(arr[i])) {
   
			result=result.concat(flatten(arr[i]))
		} else {
   
			result.push(arr[i])
		}
	}
	return result
}

去重 - 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]<arr[i-1]) {
   //向前移动到合适的位置
			[arr[i],arr[i-1]]=[arr[i-1],arr[i]]
			i--
		}
	}
}

归并排序

function sort(arr) {
   
	if 
  • 70
    点赞
  • 380
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值