20道JS原理题助你面试一臂之力(必看)
前言
本文针对目前常见的面试题,仅提供了相应的核心原理及思路,部分边界细节未处理。后续会持续更新,希望对你有所帮助。
1.实现一个call函数
//思路:将要改变this指向的方法挂到目标this上执行并返回 Function.prototype.mycall=function(context){ if(typeofthis!=='function'){ thrownewTypeError('notfunciton') } context=context||window context.fn=this letarg=[...arguments].slice(1) letresult=context.fn(...arg) deletecontext.fn returnresult }
2.实现一个apply函数
//思路:将要改变this指向的方法挂到目标this上执行并返回 Function.prototype.myapply=function(context){ if(typeofthis!=='function'){ thrownewTypeError('notfunciton') } context=context||window context.fn=this letresult if(arguments[1]){ result=context.fn(...arguments[1]) }else{ result=context.fn() } deletecontext.fn returnresult }
3.实现一个bind函数
//思路:类似call,但返回的是函数 Function.prototype.mybind=function(context){ if(typeofthis!=='function'){ thrownewTypeError('Error') } let_this=this letarg=[...arguments].slice(1) returnfunctionF(){ //处理函数使用new的情况 if(thisinstanceofF){ returnnew_this(...arg,...arguments) }else{ return_this.apply(context,arg.concat(...arguments)) } } }
4.instanceof的原理
//思路:右边变量的原型存在于左边变量的原型链上 functioninstanceOf(left,right){ letleftValue=left.__proto__ letrightValue=right.prototype while(true){ if(leftValue===null){ returnfalse } if(leftValue===rightValue){ returntrue } leftValue=leftValue.__proto__ } }
5.Object.create的基本实现原理
//思路:将传入的对象作为原型 functioncreate(obj){ functionF(){} F.prototype=obj returnnewF()
6.new本质
functionmyNew(fun){ returnfunction(){ //创建一个新对象且将其隐式原型指向构造函数原型 letobj={ __proto__:fun.prototype } //执行构造函数 fun.call(obj,...arguments) //返回该对象 returnobj } } functionperson(name,age){ this.name=name this.age=age } letobj=myNew(person)('chen',18)//{name:"chen",age:18}
7.实现一个基本的Promise
//未添加异步处理等其他边界情况 //①自动执行函数,②三个状态,③then classPromise{ constructor(fn){ //三个状态 this.state='pending' this.value=undefined this.reason=undefined letresolve=value=>{ if(this.state==='pending'){ this.state='fulfilled' this.value=value } } letreject=value=>{ if(this.state==='pending'){ this.state='rejected' this.reason=value } } //自动执行函数 try{ fn(resolve,reject) }catch(e){ reject(e) } } //then then(onFulfilled,onRejected){ switch(this.state){ case'fulfilled': onFulfilled() break case'rejected': onRejected() break default: } } }
8.实现浅拷贝
//1....实现 letcopy1={...{x:1}} //2.Object.assign实现 letcopy2=Object.assign({},{x:1})
9.实现一个基本的深拷贝
//1.JOSN.stringify()/JSON.parse() letobj={a:1,b:{x:3}} JSON.parse(JSON.stringify(obj)) //2.递归拷贝 functiondeepClone(obj){ letcopy=objinstanceofArray?[]:{} for(letiinobj){ if(obj.hasOwnProperty(i)){ copy[i]=typeofobj[i]==='object'?deepClone(obj[i]):obj[i] } } returncopy }
10.使用setTimeout模拟setInterval
//可避免setInterval因执行时间导致的间隔执行时间不一致 setTimeout(function(){ //dosomething setTimeout(arguments.callee,500) },500)
11.js实现一个继承方法
//借用构造函数继承实例属性 functionChild(){ Parent.call(this) } //寄生继承原型属性 (function(){ letSuper=function(){} Super.prototype=Parent.prototype Child.prototype=newSuper() })()
12.实现一个基本的EventBus
//组件通信,一个触发与监听的过程 classEventEmitter{ constructor(){ //存储事件 this.events=this.events||newMap() } //监听事件 addListener(type,fn){ if(!this.events.get(type)){ this.events.set(type,fn) } } //触发事件 emit(type){ lethandle=this.events.get(type) handle.apply(this,[...arguments].slice(1)) } } //测试 letemitter=newEventEmitter() //监听事件 emitter.addListener('ages',age=>{ console.log(age) }) //触发事件 emitter.emit('ages',18)//18
13.实现一个双向数据绑定
letobj={} letinput=document.getElementById('input') letspan=document.getElementById('span') //数据劫持 Object.defineProperty(obj,'text',{ configurable:true, enumerable:true, get(){ console.log('获取数据了') returnobj['text'] }, set(newVal){ console.log('数据更新了') input.value=newVal span.innerHTML=newVal } }) //输入监听 input.addEventListener('keyup',function(e){ obj.text=e.target.value })
完整实现可前往之前写的:这应该是最详细的响应式系统讲解了
14.实现一个简单路由
//hash路由 classRoute{ constructor(){ //路由存储对象 this.routes={} //当前hash this.currentHash='' //绑定this,避免监听时this指向改变 this.freshRoute=this.freshRoute.bind(this) //监听 window.addEventListener('load',this.freshRoute,false) window.addEventListener('hashchange',this.freshRoute,false) } //存储 storeRoute(path,cb){ this.routes[path]=cb||function(){} } //更新 freshRoute(){ this.currentHash=location.hash.slice(1)||'/' this.routes[this.currentHash]() } }
15.实现懒加载
letimgs=document.querySelectorAll('img') //可视区高度 letclientHeight=window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight functionlazyLoad(){ //滚动卷去的高度 letscrollTop=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop for(leti=0;i0&&x 16.rem基本设置
//原始配置 functionsetRem(){ letdoc=document.documentElement letwidth=doc.getBoundingClientRect().width letrem=width/75 doc.style.fontSize=rem+'px' } //监听窗口变化 addEventListener("resize",setRem)17.手写实现AJAX
//1.简单流程 //实例化 letxhr=newXMLHttpRequest() //初始化 xhr.open(method,url,async) //发送请求 xhr.send(data) //设置状态变化回调处理请求结果 xhr.onreadystatechange=()=>{ if(xhr.readyStatus===4&&xhr.status===200){ console.log(xhr.responseText) } } //2.基于promise实现 functionajax(options){ //请求地址 consturl=options.url //请求方法 constmethod=options.method.toLocaleLowerCase()||'get' //默认为异步true constasync=options.async //请求参数 constdata=options.data //实例化 constxhr=newXMLHttpRequest() //请求超时 if(options.timeout&&options.timeout>0){ xhr.timeout=options.timeout } //返回一个Promise实例 returnnewPromise((resolve,reject)=>{ xhr.ontimeout=()=>reject&&reject('请求超时') //监听状态变化回调 xhr.onreadystatechange=()=>{ if(xhr.readyState==4){ //200-300之间表示请求成功,304资源未变,取缓存 if(xhr.status>=200&&xhr.status<300||xhr.status==304){ resolve&&resolve(xhr.responseText) }else{ reject&&reject() } } } //错误回调 xhr.onerror=err=>reject&&reject(err) letparamArr=[] letencodeData //处理请求参数 if(datainstanceofObject){ for(letkeyindata){ //参数拼接需要通过encodeURIComponent进行编码 paramArr.push(encodeURIComponent(key)+'='+encodeURIComponent(data[key])) } encodeData=paramArr.join('&') } //get请求拼接参数 if(method==='get'){ //检测url中是否已存在?及其位置 constindex=url.indexOf('?') if(index===-1)url+='?' elseif(index!==url.length-1)url+='&' //拼接url url+=encodeData } //初始化 xhr.open(method,url,async) //发送请求 if(method==='get')xhr.send(null) else{ //post方式需要设置请求头 xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8') xhr.send(encodeData) } }) }18.实现拖拽
window.onload=function(){ //drag处于绝对定位状态 letdrag=document.getElementById('box') drag.onmousedown=function(e){ vare=e||window.event //鼠标与拖拽元素边界的距离=鼠标与可视区边界的距离-拖拽元素与边界的距离 letdiffX=e.clientX-drag.offsetLeft letdiffY=e.clientY-drag.offsetTop drag.onmousemove=function(e){ //拖拽元素移动的距离=鼠标与可视区边界的距离-鼠标与拖拽元素边界的距离 letleft=e.clientX-diffX lettop=e.clientY-diffY //避免拖拽出可视区 if(left<0){ left=0 }elseif(left>window.innerWidth-drag.offsetWidth){ left=window.innerWidth-drag.offsetWidth } if(top<0){ top=0 }elseif(top>window.innerHeight-drag.offsetHeight){ top=window.innerHeight-drag.offsetHeight } drag.style.left=left+'px' drag.style.top=top+'px' } drag.onmouseup=function(e){ this.onmousemove=null this.onmouseup=null } } }19.实现一个节流函数
//思路:在规定时间内只触发一次 functionthrottle(fn,delay){ //利用闭包保存时间 letprev=Date.now() returnfunction(){ letcontext=this letarg=arguments letnow=Date.now() if(now-prev>=delay){ fn.apply(context,arg) prev=Date.now() } } } functionfn(){ console.log('节流') } addEventListener('scroll',throttle(fn,1000))20.实现一个防抖函数
//思路:在规定时间内未触发第二次,则执行 functiondebounce(fn,delay){ //利用闭包保存定时器 lettimer=null returnfunction(){ letcontext=this letarg=arguments //在规定时间内再次触发会先清除定时器后再重设定时器 clearTimeout(timer) timer=setTimeout(function(){ fn.apply(context,arg) },delay) } } functionfn(){ console.log('防抖') } addEventListener('scroll',debounce(fn,1000))以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。