手撕前端面试代码题

目录

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中插入<script>资源请求标签

letscript=document.createElement('script')

script.src=`${url}?${params}&callback=${funcName}`

document.body.appendChild(script)

//在本地设置供后端返回数据时调用的函数

window[funcName]=data=>{

callback(data)

deletewindow[funcName]

document.body.removeChild(script)

}

}

//使用方法

jsonp('http://xxxxxxxx',{id:123},data=>{

//获取数据后的操作

})

js插入html中标签的内容

后端返回的<script>资源的内容

funcName('datadatadatadatadatadatadatadata')

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • 废话少说,本文分四个部分,css、js、知识点一、知识点二、React(部分问题没有给出答案,后续更新) css面...
    666同学阅读 1,078评论 0 0
  • 目前 JavaScript 仍是前端开发的灵魂,各种层出不穷的框架其实都是与底层相关。 开始之前,借前端三元同学的...
    WEB前端含光阅读 276评论 0 0
  • css相关 1. 万能居中 1.margin: 0 auto;水平 2.text-align: center;水平...
    chaocc阅读 942评论 0 2
  • 一、html和css部分1、如何理解CSS的盒子模型?标准盒子模型:宽度=内容的宽度(content)+ bord...
    这是这时阅读 402评论 0 5
  • 渐变的面目拼图要我怎么拼? 我是疲乏了还是投降了? 不是不允许自己坠落, 我没有滴水不进的保护膜。 就是害怕变得面...
    闷热当乘凉阅读 4,227评论 0 13