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))
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。