手撕前端面试代码题,看完吊打面试官

113 阅读5分钟

字符串

  • 去除空格 - 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<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 (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]<part2[0]?part1:part2).shift())

return [...result,...part1,...part2]

}` 

快速排序

`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) return

flag=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;i<len;i++) {

val=fn(val,this[i],i,this)//循环并迭代结果

}

return val

}

console.log([1,2,3,4,5].reduce((pre,item)=>pre+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<len;i++){

fn.call(temp_this,this[i],i,this)//循环数组元素,为回调函数传入参数

}

}` 

Promise

Promise.all()

`Promise.prototype.all=function(promiseList) {

return new Promise((resolve,reject)=>{

if (promiseList.length===0) return resolve([])

let result=[],count=0

promiseList.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

blog.csdn.net/weixin\_437…

`class Promise {

constructor(task) {

this.status="pending"

this.value=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

最后

为了帮助大家更好的了解前端,特别整理了《前端工程师面试手册》电子稿文件。

开源分享:docs.qq.com/doc/DSmRnRG…