20道JS原理題助你面試一臂之力!
本文針對(duì)目前常見(jiàn)的面試題,實(shí)現(xiàn)了相應(yīng)方法的核心原理,部分邊界細(xì)節(jié)未處理。后續(xù)也會(huì)持續(xù)更新,希望對(duì)你有所幫助。
1、實(shí)現(xiàn)一個(gè)call函數(shù)
//?思路:將要改變this指向的方法掛到目標(biāo)this上執(zhí)行并返回
Function.prototype.mycall?=?function?(context)?{
??if?(typeof?this?!==?'function')?{
????throw?new?TypeError('not?funciton')
??}
??context?=?context?||?window
??context.fn?=?this
??let?arg?=?[...arguments].slice(1)
??let?result?=?context.fn(...arg)
??delete?context.fn
??return?result
}?
2、實(shí)現(xiàn)一個(gè)apply函數(shù)
//?思路:將要改變this指向的方法掛到目標(biāo)this上執(zhí)行并返回
Function.prototype.myapply?=?function?(context)?{
??if?(typeof?this?!==?'function')?{
????throw?new?TypeError('not?funciton')
??}
??context?=?context?||?window
??context.fn?=?this
??let?result
??if?(arguments[1])?{
????result?=?context.fn(...arguments[1])
??}?else?{
????result?=?context.fn()
??}
??delete?context.fn
??return?result
}
3、實(shí)現(xiàn)一個(gè)bind函數(shù)
//?思路:類(lèi)似call,但返回的是函數(shù)
Function.prototype.mybind?=?function?(context)?{
??if?(typeof?this?!==?'function')?{
????throw?new?TypeError('Error')
??}
??let?_this?=?this
??let?arg?=?[...arguments].slice(1)
??return?function?F()?{
????//?處理函數(shù)使用new的情況
????if?(this?instanceof?F)?{
??????return?new?_this(...arg,?...arguments)
????}?else?{
??????return?_this.apply(context,?arg.concat(...arguments))
????}
??}
}
4、instanceof的原理
//?思路:右邊變量的原型存在于左邊變量的原型鏈上
function?instanceOf(left,?right)?{
??let?leftValue?=?left.__proto__
??let?rightValue?=?right.prototype
??while?(true)?{
????if?(leftValue?===?null)?{
??????return?false
????}
????if?(leftValue?===?rightValue)?{
??????return?true
????}
????leftValue?=?leftValue.__proto__
??}
}
5、Object.create的基本實(shí)現(xiàn)原理
//?思路:將傳入的對(duì)象作為原型
function?create(obj)?{
??function?F()?{}
??F.prototype?=?obj
??return?new?F()
}
6、new本質(zhì)
function?myNew?(fun)?{
??return?function?()?{
????//?創(chuàng)建一個(gè)新對(duì)象且將其隱式原型指向構(gòu)造函數(shù)原型
????let?obj?=?{
??????__proto__?:?fun.prototype
????}
????//?執(zhí)行構(gòu)造函數(shù)
????fun.call(obj,?...arguments)
????//?返回該對(duì)象
????return?obj
??}
}
function?person(name,?age)?{
??this.name?=?name
??this.age?=?age
}
let?obj?=?myNew(person)('chen',?18)?//?{name:?"chen",?age:?18}
7、實(shí)現(xiàn)一個(gè)基本的Promise
//?未添加異步處理等其他邊界情況
//?①自動(dòng)執(zhí)行函數(shù),②三個(gè)狀態(tài),③then
class?Promise?{
??constructor?(fn)?{
????//?三個(gè)狀態(tài)
????this.state?=?'pending'
????this.value?=?undefined
????this.reason?=?undefined
????let?resolve?=?value?=>?{
??????if?(this.state?===?'pending')?{
????????this.state?=?'fulfilled'
????????this.value?=?value
??????}
????}
????let?reject?=?value?=>?{
??????if?(this.state?===?'pending')?{
????????this.state?=?'rejected'
????????this.reason?=?value
??????}
????}
????//?自動(dòng)執(zhí)行函數(shù)
????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、實(shí)現(xiàn)淺拷貝
//?1.?...實(shí)現(xiàn)
let?copy1?=?{...{x:1}}
//?2.?Object.assign實(shí)現(xiàn)
let?copy2?=?Object.assign({},?{x:1})
9、實(shí)現(xiàn)一個(gè)基本的深拷貝
//?1.?JOSN.stringify()/JSON.parse()
let?obj?=?{a:?1,?b:?{x:?3}}
JSON.parse(JSON.stringify(obj))
//?2.?遞歸拷貝
function?deepClone(obj)?{
??let?copy?=?obj?instanceof?Array???[]?:?{}
??for?(let?i?in?obj)?{
????if?(obj.hasOwnProperty(i))?{
??????copy[i]?=?typeof?obj[i]?===?'object'???deepClone(obj[i])?:?obj[i]
????}
??}
??return?copy
}
10、使用setTimeout模擬setInterval
//?可避免setInterval因執(zhí)行時(shí)間導(dǎo)致的間隔執(zhí)行時(shí)間不一致
setTimeout?(function?()?{
??//?do?something
??setTimeout?(arguments.callee,?500)
},?500)
//?借用構(gòu)造函數(shù)繼承實(shí)例屬性
function?Child?()?{
??Parent.call(this)
}
//?寄生繼承原型屬性
(function?()?{
??let?Super?=?function?()?{}
??Super.prototype?=?Parent.prototype
??Child.prototype?=?new?Super()
})()
12、實(shí)現(xiàn)一個(gè)基本的Event Bus
//?組件通信,一個(gè)觸發(fā)與監(jiān)聽(tīng)的過(guò)程
class?EventEmitter?{
??constructor?()?{
????//?存儲(chǔ)事件
????this.events?=?this.events?||?new?Map()
??}
??//?監(jiān)聽(tīng)事件
??addListener?(type,?fn)?{
????if?(!this.events.get(type))?{
??????this.events.set(type,?fn)
????}
??}
??//?觸發(fā)事件
??emit?(type)?{
????let?handle?=?this.events.get(type)
????handle.apply(this,?[...arguments].slice(1))
??}
}
//?測(cè)試
let?emitter?=?new?EventEmitter()
//?監(jiān)聽(tīng)事件
emitter.addListener('ages',?age?=>?{
??console.log(age)
})
//?觸發(fā)事件
emitter.emit('ages',?18)??//?18
13、實(shí)現(xiàn)一個(gè)雙向數(shù)據(jù)綁定
let?obj?=?{}
let?input?=?document.getElementById('input')
let?span?=?document.getElementById('span')
//?數(shù)據(jù)劫持
Object.defineProperty(obj,?'text',?{
??configurable:?true,
??enumerable:?true,
??get()?{
????console.log('獲取數(shù)據(jù)了')
??},
??set(newVal)?{
????console.log('數(shù)據(jù)更新了')
????input.value?=?newVal
????span.innerHTML?=?newVal
??}
})
//?輸入監(jiān)聽(tīng)
input.addEventListener('keyup',?function(e)?{
??obj.text?=?e.target.value
})
14、實(shí)現(xiàn)一個(gè)簡(jiǎn)單路由
//?hash路由
class?Route{
??constructor(){
????//?路由存儲(chǔ)對(duì)象
????this.routes?=?{}
????//?當(dāng)前hash
????this.currentHash?=?''
????//?綁定this,避免監(jiān)聽(tīng)時(shí)this指向改變
????this.freshRoute?=?this.freshRoute.bind(this)
????//?監(jiān)聽(tīng)
????window.addEventListener('load',?this.freshRoute,?false)
????window.addEventListener('hashchange',?this.freshRoute,?false)
??}
??//?存儲(chǔ)
??storeRoute?(path,?cb)?{
????this.routes[path]?=?cb?||?function?()?{}
??}
??//?更新
??freshRoute?()?{
????this.currentHash?=?location.hash.slice(1)?||?'/'
????this.routes[this.currentHash]()
??}???
}
15、實(shí)現(xiàn)懶加載
<ul>
??<li><img?src="./imgs/default.png"?data="./imgs/1.png"?alt="">li>
??<li><img?src="./imgs/default.png"?data="./imgs/2.png"?alt="">li>
??<li><img?src="./imgs/default.png"?data="./imgs/3.png"?alt="">li>
??<li><img?src="./imgs/default.png"?data="./imgs/4.png"?alt="">li>
??<li><img?src="./imgs/default.png"?data="./imgs/5.png"?alt="">li>
??<li><img?src="./imgs/default.png"?data="./imgs/6.png"?alt="">li>
??<li><img?src="./imgs/default.png"?data="./imgs/7.png"?alt="">li>
??<li><img?src="./imgs/default.png"?data="./imgs/8.png"?alt="">li>
??<li><img?src="./imgs/default.png"?data="./imgs/9.png"?alt="">li>
??<li><img?src="./imgs/default.png"?data="./imgs/10.png"?alt="">li>
ul>
let?imgs?=??document.querySelectorAll('img')
//?可視區(qū)高度
let?clientHeight?=?window.innerHeight?||?document.documentElement.clientHeight?||?document.body.clientHeight
function?lazyLoad?()?{
??//?滾動(dòng)卷去的高度
??let?scrollTop?=?window.pageYOffset?||?document.documentElement.scrollTop?||?document.body.scrollTop
??for?(let?i?=?0;?i?
????//?圖片在可視區(qū)冒出的高度
????let?x?=?clientHeight?+?scrollTop?-?imgs[i].offsetTop
????//?圖片在可視區(qū)內(nèi)
????if?(x?>?0?&&?x?
??????imgs[i].src?=?imgs[i].getAttribute('data')
????}?
??}??????
}
//?addEventListener('scroll',?lazyLoad)?or?setInterval(lazyLoad,?1000)
16、rem實(shí)現(xiàn)原理
//?原始配置
function?setRem?()?{
??let?doc?=?document.documentElement
??let?width?=?doc.getBoundingClientRect().width
??let?rem?=?width?/?75
??doc.style.fontSize?=?rem?+?'px'
}
//?監(jiān)聽(tīng)窗口變化
addEventListener("resize",?setRem)
17、手寫(xiě)實(shí)現(xiàn)AJAX
//?1.?簡(jiǎn)單流程
//?實(shí)例化
let?xhr?=?new?XMLHttpRequest()
//?初始化
xhr.open(method,?url,?async)
//?發(fā)送請(qǐng)求
xhr.send(data)
//?設(shè)置狀態(tài)變化回調(diào)處理請(qǐng)求結(jié)果
xhr.onreadystatechange?=?()?=>?{
??if?(xhr.readyStatus?===?4?&&?xhr.status?===?200)?{
????console.log(xhr.responseText)
??}
}
//?2.?基于promise實(shí)現(xiàn)?
function?ajax?(options)?{
??//?請(qǐng)求地址
??const?url?=?options.url
??//?請(qǐng)求方法
??const?method?=?options.method.toLocaleLowerCase()?||?'get'
??//?默認(rèn)為異步true
??const?async?=?options.async
??//?請(qǐng)求參數(shù)
??const?data?=?options.data
??//?實(shí)例化
??const?xhr?=?new?XMLHttpRequest()
??//?請(qǐng)求超時(shí)
??if?(options.timeout?&&?options.timeout?>?0)?{
????xhr.timeout?=?options.timeout
??}
??//?返回一個(gè)Promise實(shí)例
??return?new?Promise?((resolve,?reject)?=>?{
????xhr.ontimeout?=?()?=>?reject?&&?reject('請(qǐng)求超時(shí)')
????//?監(jiān)聽(tīng)狀態(tài)變化回調(diào)
????xhr.onreadystatechange?=?()?=>?{
??????if?(xhr.readyState?==?4)?{
????????//?200-300?之間表示請(qǐng)求成功,304資源未變,取緩存
????????if?(xhr.status?>=?200?&&?xhr.status?300?||?xhr.status?==?304)?{
??????????resolve?&&?resolve(xhr.responseText)
????????}?else?{
??????????reject?&&?reject()
????????}
??????}
????}
????//?錯(cuò)誤回調(diào)
????xhr.onerror?=?err?=>?reject?&&?reject(err)
????let?paramArr?=?[]
????let?encodeData
????//?處理請(qǐng)求參數(shù)
????if?(data?instanceof?Object)?{
??????for?(let?key?in?data)?{
????????//?參數(shù)拼接需要通過(guò)?encodeURIComponent?進(jìn)行編碼
????????paramArr.push(encodeURIComponent(key)?+?'='?+?encodeURIComponent(data[key]))
??????}
??????encodeData?=?paramArr.join('&')
????}
????//?get請(qǐng)求拼接參數(shù)
????if?(method?===?'get')?{
??????//?檢測(cè)url中是否已存在???及其位置
??????const?index?=?url.indexOf('?')
??????if?(index?===?-1)?url?+=?'?'
??????else?if?(index?!==?url.length?-1)?url?+=?'&'
??????//?拼接url
??????url?+=?encodeData
????}
????//?初始化
????xhr.open(method,?url,?async)
????//?發(fā)送請(qǐng)求
????if?(method?===?'get')?xhr.send(null)
????else?{
??????//?post?方式需要設(shè)置請(qǐng)求頭
??????xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8')
??????xhr.send(encodeData)
????}
??})
}
18、實(shí)現(xiàn)拖拽
window.onload?=?function?()?{
??//?drag處于絕對(duì)定位狀態(tài)
??let?drag?=?document.getElementById('box')
??drag.onmousedown?=?function(e)?{
????var?e?=?e?||?window.event
????//?鼠標(biāo)與拖拽元素邊界的距離?=?鼠標(biāo)與可視區(qū)邊界的距離?-?拖拽元素與邊界的距離
????let?diffX?=?e.clientX?-?drag.offsetLeft
????let?diffY?=?e.clientY?-?drag.offsetTop
????drag.onmousemove?=?function?(e)?{
??????//?拖拽元素移動(dòng)的距離?=?鼠標(biāo)與可視區(qū)邊界的距離?-?鼠標(biāo)與拖拽元素邊界的距離
??????let?left?=?e.clientX?-?diffX
??????let?top?=?e.clientY?-?diffY
??????//?避免拖拽出可視區(qū)
??????if?(left?0)?{
????????left?=?0
??????}?else?if?(left?>?window.innerWidth?-?drag.offsetWidth)?{
????????left?=?window.innerWidth?-?drag.offsetWidth
??????}
??????if?(top?0)?{
????????top?=?0
??????}?else?if?(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、實(shí)現(xiàn)一個(gè)節(jié)流函數(shù)
//?思路:在規(guī)定時(shí)間內(nèi)只觸發(fā)一次
function?throttle?(fn,?delay)?{
??//?利用閉包保存時(shí)間
??let?prev?=?Date.now()
??return?function?()?{
????let?context?=?this
????let?arg?=?arguments
????let?now?=?Date.now()
????if?(now?-?prev?>=?delay)?{
??????fn.apply(context,?arg)
??????prev?=?Date.now()
????}
??}
}
function?fn?()?{
??console.log('節(jié)流')
}
addEventListener('scroll',?throttle(fn,?1000))?
20、實(shí)現(xiàn)一個(gè)防抖函數(shù)
//?思路:在規(guī)定時(shí)間內(nèi)未觸發(fā)第二次,則執(zhí)行
function?debounce?(fn,?delay)?{
??//?利用閉包保存定時(shí)器
??let?timer?=?null
??return?function?()?{
????let?context?=?this
????let?arg?=?arguments
????//?在規(guī)定時(shí)間內(nèi)再次觸發(fā)會(huì)先清除定時(shí)器后再重設(shè)定時(shí)器
????clearTimeout(timer)
????timer?=?setTimeout(function?()?{
??????fn.apply(context,?arg)
????},?delay)
??}
}
function?fn?()?{
??console.log('防抖')
}
addEventListener('scroll',?debounce(fn,?1000))?
源自:https://juejin.im/post/5d2ee123e51d4577614761f8
聲明:文章著作權(quán)歸作者所有,如有侵權(quán),請(qǐng)聯(lián)系小編刪除。
掃碼關(guān)注公眾號(hào),訂閱更多精彩內(nèi)容。

評(píng)論
圖片
表情
