JS面試必備之手寫instanceof,深拷貝,節(jié)流和防抖
一、instanceof 的實現(xiàn)
使用 instanceof 操作符,如果一個實例的原型鏈中出現(xiàn)過相應(yīng)的構(gòu)造函數(shù)的原型,則 instanceof 返回 true。
instanceof 主要的實現(xiàn)原理就是只要右邊變量的 prototype 在左邊變量的原型鏈上即可。 因此,instanceof 在查找的過程中會遍歷左邊變量的原型鏈,直到找到右邊變量的 prototype,如果查找失敗,則會返回 false,告訴我們左邊變量并非是右邊變量的實例。
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;
}測試代碼:
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;
}測試代碼:
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)引用問題:
// 解決循環(huá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)用時,weakMap為空,不會走上面那個if(hash.has())語句,如果待拷貝對象中有屬性也為對象時,
// 則將該待拷貝對象存入weakMap中,此時的健值和健名都是對該待拷貝對象的引用
hash.set(obj, obj)
result[key] = deepClone2(obj[key], hash);
}
}
}
return result;
}測試代碼:
var data2 = {
name: 'foo',
child: null,
}
data2.child = data2;
console.log(deepClone2(data2));四、函數(shù)防抖
- 當(dāng)持續(xù)觸發(fā)事件時,一定時間內(nèi)沒有再觸發(fā)事件,事件處理函數(shù)才會執(zhí)行一次;
- 如果在設(shè)定的時間來到之前,又一次觸發(fā)了事件,則會取消上次事件,重新開始計時;
使用場景:resize、scroll、輸入框內(nèi)容校驗等。
function debounce(handle,wait) {
let timer = null;
return function () {
if(timer != null) {
clearTimeout(timer);
}
timer = setTimeout(handle, wait);
}
}五、節(jié)流
當(dāng)持續(xù)觸發(fā)事件時,保證一定時間內(nèi)只調(diào)用一次事件處理函數(shù)。通俗解釋就比如:我們把水龍頭打開,水嘩嘩的往外流,秉著節(jié)約的原則,我們要把水龍頭關(guān)小,最好是如我們的心愿按照一定的規(guī)律,在某個時間內(nèi)一滴一滴的往下滴。
時間戳版本:
function throttle (handle,wait) {
let prev = Date.now();
return function() {
let current = Date.now();
if(current - prev >= wait) {
handle();
prev = Date.now();
}
}
}定時器版本:
- 當(dāng)觸發(fā)事件的時候,我們設(shè)置一個定時器;
- 當(dāng)再次觸發(fā)的時候,如果定時器存在,就不執(zhí)行,直到 wait 時間后,定時器執(zhí)行 handle 函數(shù),并且清空定時器,這樣就可以設(shè)置下一個定時器;
- 當(dāng)?shù)谝淮斡|發(fā)事件時,不會立即執(zhí)行行數(shù),而是在 delay 秒之后才執(zhí)行,而后在怎么頻繁觸發(fā)也都是在 wait 時間才執(zhí)行一次。
function throttle1(handle, wait) {
let timer = null;
return function() {
if(!timer) {
timer = setTimeout(() => {
handle();
timer = null;
},wait);
}
}
}定時器 + 時間戳版本:
- 節(jié)流中使用時間戳和定時器版本都是可以的,更精確的,可以使用時間戳和定時器相結(jié)合,當(dāng)?shù)谝淮问录|發(fā)時馬上執(zhí)行事件處理函數(shù),最后一次觸發(fā)時也還會執(zhí)行一次事件處理函數(shù)。
- 在節(jié)流函數(shù)內(nèi)部使用了 pre 和 current 與 wait 來計算剩余時間 remaining,當(dāng) remaining <= 0 時,表示執(zhí)行該執(zhí)行事件處理函數(shù)了(保證了第一次觸發(fā)事件就能立即執(zhí)行事件處理函數(shù)和每隔 wait 時間執(zhí)行一次事件處理函數(shù))。
- 如果還沒到時間的話就設(shè)定在remaining時間后再觸發(fā) (保證了最后一次觸發(fā)事件后還能再執(zhí)行一次事件處理函數(shù))。當(dāng)然在 remaining 這段時間中如果又一次觸發(fā)事件,那么會取消當(dāng)前的計時器,并重新計算一個remaining來判斷當(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ù)防抖:將幾次操作合并為一次操作進行。原理是維護一個計時器,規(guī)定在delay時間后觸發(fā)函數(shù),但是在delay 時間內(nèi)再次觸發(fā)的話,就會取消之前的計時器而重新設(shè)置。這樣一來,只有最后一次操作能被觸發(fā)。
- 函數(shù)節(jié)流:使得一定時間內(nèi)只觸發(fā)一次函數(shù)。原理是通過判斷是否到達一定時間來觸發(fā)函數(shù)。
- 區(qū)別: 函數(shù)節(jié)流不管事件觸發(fā)有多頻繁,都會保證在規(guī)定時間內(nèi)一定會執(zhí)行一次真正的事件處理函數(shù),而函數(shù)防抖只是在最后一次事件后才觸發(fā)一次函數(shù)。比如在頁面的無限加載場景下,我們需要用戶在滾動頁面時,每隔一段時間發(fā)一次 Ajax 請求,而不是在用戶停下滾動頁面操作時才去請求數(shù)據(jù)。這樣的場景,就適合用節(jié)流技術(shù)來實現(xiàn)。
以上就是JS面試必備之手寫instanceof,深拷貝,節(jié)流和防抖的詳細(xì)內(nèi)容,更多關(guān)于JS面試的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript實現(xiàn)上傳圖片并預(yù)覽的效果實現(xiàn)代碼
圖片上傳預(yù)覽,就是在使用文件選擇框選擇了文件之后就可以在頁面上看見圖片的效果,關(guān)于這個效果我一直認(rèn)為是無法做到的2011-04-04
h5后臺切換檢測利用visibilitychange的缺點超詳細(xì)分析
在移動端的H5應(yīng)用場景中,用戶經(jīng)常會將頁面切到后臺再切回前臺,瀏覽器對后臺頁面的資源調(diào)度、定時任務(wù)執(zhí)行和網(wǎng)絡(luò)請求策略會產(chǎn)生顯著變化,這篇文章主要介紹了h5后臺切換檢測利用visibilitychange的缺點分析的相關(guān)資料,需要的朋友可以參考下2026-03-03
關(guān)于vite?+?ts?找不到模塊@/xxxx?或其相應(yīng)的類型聲明問題
這篇文章主要介紹了vite?+?ts?找不到模塊@/xxxx?或其相應(yīng)的類型聲明,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-06-06

