手撕前端面试代码题

目录

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

lettarget=event.target||event.srcElement

//可以自己打印一下event.target.nodeName,看看是什么

if(target.nodeName.toLowerCase()==='xxx'){

//事件内容

}

}

数组 对象

扁平化

functionflatten(arr){

letresult=[]

for(leti=0,len=arr.length;i

if(Array.isArray(arr[i])) {

result=result.concat(flatten(arr[i]))

}else{

result.push(arr[i])

}

}

returnresult

}

去重 - unique()

functionunique(arr){

letappeard=newSet()

returnarr.filter(item=>{

//创建一个可以唯一标识对象的字符串id

letid=item+JSON.stringify(item)

if(appeard.has(id)) {

returnfalse

}else{

appeard.add(id)

returntrue

}

})

}

拷贝

浅拷贝

functioncopy(obj){

letresult=Array.isArray(obj)?[]:{}

Object.keys(obj).forEach(key=>result[key]=obj[key])

returnresult

}

otherStar={...star}

Object.assign({},star)

深拷贝

copy()函数实现

处理了循环引用key为symbol类型的情况

functioncopy(obj,appeard=new Map()){

if(!(objinstanceofObject))returnobj//如果是原始数据类型

if(appeard.has(obj))returnappeard.get(obj)//如果已经出现过

letresult=Array.isArray(obj)?[]:{}

appeard.set(obj,result)//将新对象放入map

//遍历所有属性进行递归拷贝

;[...Object.keys(obj),...Object.getOwnPropertySymbols(obj)]

.forEach(key=>result[key]=copy(obj[key],appeard))

returnresult

}

JSON.stringify

只能处理纯JSON数据

有几种情况会发生错误

包含不能转成 JSON 格式的数据

循环引用

undefined,NaN, -Infinity, Infinity 都会被转化成null

RegExp/函数不会拷贝

new Date()会被转成字符串

new=JSON.parse(JSON.stringify(old))

字符串

去除空格 - trim()

functionmyTrim(str){

returnstr.replace(/(^\s+)|(\s+$)/g,'')//将前空格和后空格替换为空

}

functionmyTrim(str){//记录前后空格的个数,最后对字符串进行截取

letfirst=0,last=str.length

for(letiinstr) {

if(str[i]===' ') {

first++

}else{

break

}

}

for(leti=last;i>first;i--) {

if(str[i]===' ') {

last--

}else{

break

}

}

returnstr.substr(first,last-first)

}

字符串全排列

广度优先实现

functioncombine(str){//抽出一个字符s,对其余的进行排列,将s放在每种排列开头

if(str.length===1)return[str]

letresults=[]

for(letiinstr) {

for(letsofcombine(str.slice(0,i)+str.slice(1+(+i)))) {

results.push(str[i]+s)

}

}

//可能会出现类似"aa"=>[aa,aa,aa,aa]的情况,需要去重

return[...newSet(results)]

}

深度优先实现

functioncombine(str){//记录已经使用过的字符,深度优先访问所有方案

letresult=[]

;(function_combine(str,path=''){

if(str.length===0)returnresult.push(path)

for(letiinstr) {

_combine(str.slice(0,i)+str.slice((+i)+1,str.length),path+str[i])

}

})(str)

//可能会出现类似"aa"=>[aa,aa,aa,aa]的情况,需要去重

return[...newSet(result)]

}

排序和查找

插入排序

functionsort(arr){//原地

for(letiinarr) {//选一个元素

while(i>0&&arr[i]

[arr[i],arr[i-1]]=[arr[i-1],arr[i]]

i--

}

}

}

归并排序

functionsort(arr){

if(arr.length===1)returnarr

//分成两部分

letmid=Math.floor(arr.length/2)

let[part1,part2]=[sort(arr.slice(0,mid)),sort(arr.slice(mid))]

//对比+合并

letresult=[]

while(part1.length>0&&part2.length>0)

result.push((part1[0]

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

}

快速排序

functionsort(arr){

if(arr.length<=1)returnarr

//选基准值

letmid_pos=arr.length>>1

letmid=arr.splice(mid_pos,1)[0]

letleft=[],right=[]

//和基准值比较,分别插入left,right数组

arr.forEach(item=>(item<=mid?left:right).push(item))

return[...sort(left),mid,...sort(right)]//递归调用排序

}

二分查找

functionsearch(arr,target){//循环写法,不断移动左右指针,缩小范围

letleft=0,right=arr.length-1

while(left<=right) {

constmid_pos=Math.floor((left+right)/2)

constmid_val=arr[mid_pos]

if(target===mid_val) {

returnmid_pos

}elseif(target>mid_val) {

left=mid_pos+1

}else{

right=mid_pos-1

}

}

return-1

}

找出出现次数最多的元素 - getMostItem()

functiongetMost(arr){

//计数

letmap=newMap()

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)

}

})

returnmax_vals

}

console.log(getMost(['1','2','3','3','55','3','55','55']))

功能函数实现

setTimeout实现setInterval

functionmyInterval(fn,interval,...args){

letcontext=this

setTimeout(()=>{

fn.apply(context,args)

myInterval(fn,interval,...args)//别忘了为它传入参数

},interval)

}

myInterval((num)=>console.log(num),500,10)

函数柯里化

functionsum(...args1){

returnfunction(...args2){

return[...args1,...args2].reduce((p,n)=>p+n)

}

}

console.log(sum(1,2,2)(7))

防抖 节流

实现了两个加工方法,返回一个加工后的防抖/节流函数

防抖

functiondebounce(fn,delay){

lettimer=null

returnfunction(){

if(timer) clearTimeout(timer)

timer=setTimeout(()=>fn.call(...arguments),delay)//别忘了为它传入参数

}

}

节流

functionthrottle(fn,delay){

letflag=true

returnfunction(){

if(!flag)return

flag=false

setTimeout(()=>{

fn(...arguments)//别忘了为它传入参数

flag=true

},delay)

}

}

数据结构

单链表

functionNode(element){//结点类

[this.element,this.next]=[element,null]

}

classLinkList{//链表类

constructor() {

this.length=0

this.head=newNode()

this.tail=newNode()

this.head.next=this.tail

}

get_all() {

letresult=[]

letnow=this.head

while(now.next!==this.tail) {

now=now.next

result.push(now.element)

}

returnresult

}

unshift(element) {//开头添加

letnode=newNode(element)

node.next=this.head.next

this.head.next=node

}

shift(){//开头删除

letnode=this.head.next

this.head.next=this.head.next.next

returnnode.element

}

}

letlist=newLinkList()

list.unshift(15)

list.unshift(16)

list.unshift(17)

console.log(list.shift())//17

console.log(list.get_all())//[ 16, 15 ]

设计模式

发布订阅模式

classObserver{

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设置函数属性

letresult=context.fn(...args)//调用函数

deletecontext.fn//删除context的函数属性

returnresult

}

call()

//除了...args

//和apply都一样

Function.prototype.myCall=function(context,...args){

context.fn=this

letresult=context.fn(...args)

deletecontext.fn

returnresult

}

bind()

Function.prototype.myBind=function(context,args1){//使用[闭包+apply]实现

return(...args2)=>this.apply(context,[...args1,...args2]);

}

InstanceOf

functionmyInstanceOf(son,father){//沿着父亲的原型链向上查找是否有儿子的原型

while(true) {

son=son.__proto__

if(!son)returnfalse

if(son===father.prototype)returntrue

}

}

myInstanceOf([],Array)// true

new

functionmyNew(constructor_fn,...args){

//构造新的空对象

letnew_obj={}

new_obj.__proto__=constructor_fn.prototype

letresult=constructor_fn.apply(new_obj,args)

//如果构造函数没有返回一个对象,则返回新创建的对象

//如果构造函数返回了一个对象,则返回那个对象

//如果构造函数返回原始值,则当作没有返回对象

returnresultinstanceofObject?result:new_obj

}

functionAnimal(name){

this.name = name;

}

letanimal = 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(leti=idx,len=this.length;i

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

}

returnval

}

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(leti=0,len=this.length;i

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

}

}

Promise

Promise.all()

Promise.prototype.all=function(promiseList){

returnnewPromise((resolve,reject)=>{

if(promiseList.length===0)returnresolve([])

letresult=[],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

https://blog.csdn.net/weixin_43758603/article/details/109641486

classPromise{

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') {

letpromise2=newPromise((resolve,reject)=>{

setTimeout(()=>{

try{

if(!this._isFunction(onFulfilled)) {

resolve(this.value)

}else{

this._resolvePromise(promise2,onFulfilled(this.value))

}

}catch(error) {

reject(error)

}

},0)

})

returnpromise2

}elseif(this.status==='rejected') {

letpromise2=newPromise((resolve,reject)=>{

setTimeout(()=>{

try{

if(!this._isFunction(onRejected)) {

reject(this.reason)

}else{

this._resolvePromise(promise2,onRejected(this.reason))

}

}catch(error) {

reject(error)

}

},0)

})

returnpromise2

}elseif(this.status==='pending')  {

letpromise2=newPromise((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)

}

})

})

returnpromise2

}

}

catch=onRejected=>this.then(null,onRejected)

finally=onFinished=>this.then(onFinished,onFinished)

staticdeferred(){

letdeferred={}

deferred.promise=newPromise((resolve,reject)=>{

deferred.resolve=resolve

deferred.reject=reject

})

returndeferred

}

staticresolve(value) {

if(valueinstanceofPromise)returnvalue

returnnewPromise(resolve=>resolve(value))

}

staticreject=reason=>{returnnewPromise((resolve, reject)=>reject(reason))}

staticall(promiseList) {

returnnewPromise((resolve,reject)=>{

if(promiseList.length===0)returnresolve([])

letresult=[],count=0

promiseList.forEach((promise,index)=>{

Promise.resolve(promise).then(value=>{

result[index]=value

if(++count===promiseList.length) resolve(result)

},reason=>reject(reason))

})

})

}

staticrace(promiseList) {

returnnewPromise((resolve,reject)=>{

if(promiseList.length===0)returnresolve()

promiseList.forEach(promise=>{

Promise.resolve(promise)

.then(value=>resolve(value),reason=>reject(reason))

})

})

}

staticallSettled(promiseList) {

returnnewPromise(resolve=>{

letresult=[],count=0

if(len===0)returnresolve(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 = 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(newTypeError('cant be the same'))

return

}

if(xinstanceofPromise) {

if(x.status==='fulfilled') {

promise._resolve(x.value)

}elseif(x.status==='rejected') {

promise._reject(x.reason)

}elseif(x.status==='pending') {

x.then(value=>{

this._resolvePromise(promise,value)

},reason=>{

promise._reject(reason)

})

}

return

}

if(this._isObject(x)||this._isFunction(x)) {

letthen

try{

then=x.then

}catch(error) {

promise._reject(error)

return

}

if(this._isFunction(then)) {

letcalled=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封装

functionajax(method,url,params,callback){

//对参数进行处理

method=method.toUpperCase()

letpost_params=null

letget_params=''

if(method==='GET') {

if(typeofparams==='object') {

lettempArr=[]

for(letkeyinparams) {

tempArr.push(`${key}=${params[key]}`)

}

params=tempArr.join('&')

}

get_params=`?${params}`

}else{

post_params=params

}

//发请求

letxhr=newXMLHttpRequest()

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

functionjsonp(url, params_obj, callback){

//创建一个供后端返回数据调用的函数名

letfuncName ='jsonp_'+ Data.now() +Math.random().toString().substr(2,5)

//将参数拼接成字符串

if(typeofparams==='object') {

lettemp=[]

for(letkeyinparams) {

temp.push(`${key}=${params[key]}`)

}

params=temp.join('&')

}

//在html中插入

你可能感兴趣的:(手撕前端面试代码题)