javascript面试题

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'){
        //事件内容
    }
}

数组 对象

扁平化

 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
https://blog.csdn.net/weixin_43758603/article/details/109641486

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
 }
 static resolve(value) {
  if (value instanceof Promise) return value
  return new Promise(resolve=>resolve(value))
 }
 static reject=reason=>{return new Promise((resolve, reject)=>reject(reason))}

 static all(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))
   })
  })
 }
 static race(promiseList) {
  return new Promise((resolve,reject)=>{
   if (promiseList.length===0) return resolve()
   promiseList.forEach(promise=>{
    Promise.resolve(promise)
     .then(value=>resolve(value),reason=>reject(reason))
   })
  })
 }
 static allSettled(promiseList) {
  return new Promise(resolve=>{
   let result=[],count=0
   if (len===0) return resolve(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(new TypeError('cant be the same'))
      return
  }
  if (x instanceof Promise) {
   if (x.status==='fulfilled') {
    promise._resolve(x.value)
   } else if (x.status==='rejected') {
    promise._reject(x.reason)
   } else if (x.status==='pending') {
    x.then(value=>{
     this._resolvePromise(promise,value)
    },reason=>{
     promise._reject(reason)
    })
   }
   return
  }
  if (this._isObject(x)||this._isFunction(x)) {
   let then
   try {
    then=x.then
   } catch (error) {
    promise._reject(error)
    return
   }
   if (this._isFunction(then)) {
    let called=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封装

function ajax(method,url,params,callback) {
 //对参数进行处理
 method=method.toUpperCase()
 let post_params=null
 let get_params=''
 
 if (method==='GET') {
  if (typeof params==='object') {
   let tempArr=[]
   for (let key in params) {
    tempArr.push(`${key}=${params[key]}`)
   }
   params=tempArr.join('&')
  }
  get_params=`?${params}`
 } else {
  post_params=params
 }

 //发请求
 let xhr=new XMLHttpRequest()

 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

function jsonp(url, params_obj, callback) {
 //创建一个供后端返回数据调用的函数名
 let funcName = 'jsonp_' + Data.now() + Math.random().toString().substr(2, 5)

 //将参数拼接成字符串
 if (typeof params==='object') {
  let temp=[]
  for (let key in params) {
   temp.push(`${key}=${params[key]}`)
  }
  params=temp.join('&')
 }

 //在html中插入<script>资源请求标签
 let script=document.createElement('script')
 script.src=`${url}?${params}&callback=${funcName}`
 document.body.appendChild(script)

 //在本地设置供后端返回数据时调用的函数
 window[funcName]=data=>{
  callback(data)

  delete window[funcName]
  document.body.removeChild(script)
 }
}

//使用方法
jsonp('http://xxxxxxxx',{id:123},data=>{
 //获取数据后的操作
})
js插入html中标签的内容

<script src="https://www.liuzhuocheng.com?callback=funcName"></script>
后端返回的<script>资源的内容

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

推荐阅读更多精彩内容