写在前面
最近我终于找到了一份满意的工作.准备面试的过程中,我整理出了一些有用的笔记,这篇就是其中之一.
既然写好了,不妨就放在这里分享给大家.
面试通常都有现场写代码的题目,我基本每次都或多或少的翻车.有意思的是,每次面试结束,自己改不到五分钟就调试出来了.
所以面试中的写代码的过程,一定不能紧张,要沉住气慢慢来.只要不是系统自动检查结果,只要是面试官看着你写,就有很大的表现的机会,哪怕最后做不出来.
我参加的最烦人的面试,是那种系统判定结果的面试,只要做不出来,就绝对不可能通过.
仔细想想,在二三十场面试中,很少有我完整写出毫无瑕疵的答案的题目,但基本也都顺利通过了.
对比来看,我的同学中,答案能运行成功但面试没通过的也大有人在.
可以肯定的是,面试官不会只根据是否能运行成功来评价应聘者.
所以,只需要顺着正确思路稳稳地做就好了,不要怕最后运行不成功.
如果实在做不出,也一定要和面试官说清你当前的进展和思路,而不是一句"我不会"就想结束问题.
当然,我指的只是前端这个对算法能力要求不强的岗位…
有一场百度的面试我甚至直接和面试官说,我是个偏感性的人,喜欢但是不擅长做算法.都给我通过了.
总的来看,如果能完全掌握这篇文章的内容,就足以应付所有前端面试中的手撕代码环节了.
来都来了,点个赞呗
转载请注明出处
有问题请直接且具体地指出,不要光说xxx有问题
导读
关于这篇文章,有几点我想先说清楚,方便读者更顺利的学习.
这篇文章不适合前端小白阅读,需要对
JS
和ES6
有一定了解,否则遇到一些写法可能不太看得懂因为精力有限,我只加了较为粗略但足以帮助读者理解的注释,因为多数题也只有几行代码而已.
如果遇到还不懂的地方,我认为读者完全可以自己去查询文档来了解为什么这么做,为什么使用这个函数.
或者,先查询该问题通常的解决思路,再回来参考我的实现
代码大量使用了
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