JS面試必備之手寫(xiě)instanceof,深拷貝,節(jié)流和防抖
一、instanceof 的實(shí)現(xiàn)
使用 instanceof 操作符,如果一個(gè)實(shí)例的原型鏈中出現(xiàn)過(guò)相應(yīng)的構(gòu)造函數(shù)的原型,則 instanceof 返回 true。
instanceof 主要的實(shí)現(xiàn)原理就是只要右邊變量的 prototype 在左邊變量的原型鏈上即可。 因此,instanceof 在查找的過(guò)程中會(huì)遍歷左邊變量的原型鏈,直到找到右邊變量的 prototype,如果查找失敗,則會(huì)返回 false,告訴我們左邊變量并非是右邊變量的實(shí)例。
function instanceof1(L,R) { L = L.__proto__; while(L != null) { if(L === R.prototype) { return true; } L = L.__proto__; } return false; }
二、淺拷貝
// 淺拷貝 function shallowClone(obj) { const result = {}; for(let key in obj) { console.log('ddddd',key); if(obj.hasOwnProperty(key)) { result[key] = obj[key]; } } return result; }
測(cè)試代碼:
let obj = { b: { c: [1, 5, 11, 23, 422] }, d: function() { console.log('hello world'); } }; const result = shallowClone(obj); console.log(result); const newObj = Object.assign({},obj); console.log(newObj);
三、深拷貝
// 深拷貝 function deepClone(obj) { if(typeof obj != 'object' || obj == null) { return obj; } const result = Array.isArray(obj) ? [] : {}; for(let key in obj) { // 自有屬性 if(obj.hasOwnProperty(key)) { const type = typeof obj[key]; if(type == 'object' && obj[key] != null) { result[key] = deepClone(obj[key]); } else { result[key] = obj[key]; } } } return result; }
測(cè)試代碼:
var data = { age: 18, name: "liuruchao", education: ["小學(xué)", "初中", "高中", "大學(xué)", undefined, null], likesFood: new Set(["fish", "banana"]), friends: [ { name: "summer", sex: "woman"}, { name: "daWen", sex: "woman"}, { name: "yang", sex: "man" } ], work: { time: "2019", project: { name: "test",obtain: ["css", "html", "js"]} }, play: function() { console.log("玩滑板"); } } console.log(deepClone(data));
解決循環(huán)引用問(wèn)題:
// 解決循環(huán)引用問(wèn)題 function deepClone2(obj, hash = new WeakMap()) { const type = typeof obj; if(type != 'object' || obj == null) { return obj; } if(hash.has(obj)) { return hash.get(obj); } const result = Array.isArray(obj) ? [] : {}; for(let key in obj) { if(obj.hasOwnProperty(key)) { if(typeof obj[key] != 'object' || obj[key] == null) { result[key] = obj[key]; } else { // 首次調(diào)用時(shí),weakMap為空,不會(huì)走上面那個(gè)if(hash.has())語(yǔ)句,如果待拷貝對(duì)象中有屬性也為對(duì)象時(shí), // 則將該待拷貝對(duì)象存入weakMap中,此時(shí)的健值和健名都是對(duì)該待拷貝對(duì)象的引用 hash.set(obj, obj) result[key] = deepClone2(obj[key], hash); } } } return result; }
測(cè)試代碼:
var data2 = { name: 'foo', child: null, } data2.child = data2; console.log(deepClone2(data2));
四、函數(shù)防抖
- 當(dāng)持續(xù)觸發(fā)事件時(shí),一定時(shí)間內(nèi)沒(méi)有再觸發(fā)事件,事件處理函數(shù)才會(huì)執(zhí)行一次;
- 如果在設(shè)定的時(shí)間來(lái)到之前,又一次觸發(fā)了事件,則會(huì)取消上次事件,重新開(kāi)始計(jì)時(shí);
使用場(chǎng)景:resize、scroll、輸入框內(nèi)容校驗(yàn)等。
function debounce(handle,wait) { let timer = null; return function () { if(timer != null) { clearTimeout(timer); } timer = setTimeout(handle, wait); } }
五、節(jié)流
當(dāng)持續(xù)觸發(fā)事件時(shí),保證一定時(shí)間內(nèi)只調(diào)用一次事件處理函數(shù)。通俗解釋就比如:我們把水龍頭打開(kāi),水嘩嘩的往外流,秉著節(jié)約的原則,我們要把水龍頭關(guān)小,最好是如我們的心愿按照一定的規(guī)律,在某個(gè)時(shí)間內(nèi)一滴一滴的往下滴。
時(shí)間戳版本:
function throttle (handle,wait) { let prev = Date.now(); return function() { let current = Date.now(); if(current - prev >= wait) { handle(); prev = Date.now(); } } }
定時(shí)器版本:
- 當(dāng)觸發(fā)事件的時(shí)候,我們?cè)O(shè)置一個(gè)定時(shí)器;
- 當(dāng)再次觸發(fā)的時(shí)候,如果定時(shí)器存在,就不執(zhí)行,直到 wait 時(shí)間后,定時(shí)器執(zhí)行 handle 函數(shù),并且清空定時(shí)器,這樣就可以設(shè)置下一個(gè)定時(shí)器;
- 當(dāng)?shù)谝淮斡|發(fā)事件時(shí),不會(huì)立即執(zhí)行行數(shù),而是在 delay 秒之后才執(zhí)行,而后在怎么頻繁觸發(fā)也都是在 wait 時(shí)間才執(zhí)行一次。
function throttle1(handle, wait) { let timer = null; return function() { if(!timer) { timer = setTimeout(() => { handle(); timer = null; },wait); } } }
定時(shí)器 + 時(shí)間戳版本:
- 節(jié)流中使用時(shí)間戳和定時(shí)器版本都是可以的,更精確的,可以使用時(shí)間戳和定時(shí)器相結(jié)合,當(dāng)?shù)谝淮问录|發(fā)時(shí)馬上執(zhí)行事件處理函數(shù),最后一次觸發(fā)時(shí)也還會(huì)執(zhí)行一次事件處理函數(shù)。
- 在節(jié)流函數(shù)內(nèi)部使用了 pre 和 current 與 wait 來(lái)計(jì)算剩余時(shí)間 remaining,當(dāng) remaining <= 0 時(shí),表示執(zhí)行該執(zhí)行事件處理函數(shù)了(保證了第一次觸發(fā)事件就能立即執(zhí)行事件處理函數(shù)和每隔 wait 時(shí)間執(zhí)行一次事件處理函數(shù))。
- 如果還沒(méi)到時(shí)間的話就設(shè)定在remaining時(shí)間后再觸發(fā) (保證了最后一次觸發(fā)事件后還能再執(zhí)行一次事件處理函數(shù))。當(dāng)然在 remaining 這段時(shí)間中如果又一次觸發(fā)事件,那么會(huì)取消當(dāng)前的計(jì)時(shí)器,并重新計(jì)算一個(gè)remaining來(lái)判斷當(dāng)前狀態(tài)。
function throttle2 (handle, wait) { let pre = Date.now(); let timer = null; return function() { let current = Date.now(); let remaining = wait - (current - pre); clearTimeout(timer); if(remaining <= 0) { handle(); pre = Date.now(); } else { timer = setTimeout(handle, remaining); } } }
節(jié)流和防抖總結(jié):
- 函數(shù)防抖:將幾次操作合并為一次操作進(jìn)行。原理是維護(hù)一個(gè)計(jì)時(shí)器,規(guī)定在delay時(shí)間后觸發(fā)函數(shù),但是在delay 時(shí)間內(nèi)再次觸發(fā)的話,就會(huì)取消之前的計(jì)時(shí)器而重新設(shè)置。這樣一來(lái),只有最后一次操作能被觸發(fā)。
- 函數(shù)節(jié)流:使得一定時(shí)間內(nèi)只觸發(fā)一次函數(shù)。原理是通過(guò)判斷是否到達(dá)一定時(shí)間來(lái)觸發(fā)函數(shù)。
- 區(qū)別: 函數(shù)節(jié)流不管事件觸發(fā)有多頻繁,都會(huì)保證在規(guī)定時(shí)間內(nèi)一定會(huì)執(zhí)行一次真正的事件處理函數(shù),而函數(shù)防抖只是在最后一次事件后才觸發(fā)一次函數(shù)。比如在頁(yè)面的無(wú)限加載場(chǎng)景下,我們需要用戶在滾動(dòng)頁(yè)面時(shí),每隔一段時(shí)間發(fā)一次 Ajax 請(qǐng)求,而不是在用戶停下滾動(dòng)頁(yè)面操作時(shí)才去請(qǐng)求數(shù)據(jù)。這樣的場(chǎng)景,就適合用節(jié)流技術(shù)來(lái)實(shí)現(xiàn)。
以上就是JS面試必備之手寫(xiě)instanceof,深拷貝,節(jié)流和防抖的詳細(xì)內(nèi)容,更多關(guān)于JS面試的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JS簡(jiǎn)單實(shí)現(xiàn)禁止訪問(wèn)某個(gè)頁(yè)面的方法
這篇文章主要介紹了JS簡(jiǎn)單實(shí)現(xiàn)禁止訪問(wèn)某個(gè)頁(yè)面的方法,涉及基本的頁(yè)面跳轉(zhuǎn)操作技巧,需要的朋友可以參考下2016-09-09JavaScript獲取多個(gè)數(shù)組的交集簡(jiǎn)單實(shí)例
這篇文章介紹了JavaScript獲取多個(gè)數(shù)組的交集簡(jiǎn)單實(shí)例,有需要的朋友可以參考一下2013-11-11js實(shí)現(xiàn)簡(jiǎn)單手風(fēng)琴效果
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)簡(jiǎn)單手風(fēng)琴效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09原生javascript實(shí)現(xiàn)圖片彈窗交互效果
這篇文章主要介紹了原生javascript實(shí)現(xiàn)圖片彈窗交互效果的方法及的相關(guān)資料,需要的朋友可以參考下2015-01-01圖片動(dòng)畫(huà)橫條廣告帶上下滾動(dòng)可自定義圖片、鏈接等等
可以自定義廣告的圖片、鏈接、長(zhǎng)、寬等。光標(biāo)移到圖片上會(huì)出現(xiàn)左右箭頭,感興趣的朋友可以嘗試測(cè)試下2013-10-10