欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS面試必備之手寫(xiě)instanceof,深拷貝,節(jié)流和防抖

 更新時(shí)間:2023年05月19日 10:28:44   作者:feelingHy  
JavaScript如何實(shí)現(xiàn)手寫(xiě)instanceof、深拷貝、節(jié)流、防抖也是面試時(shí)常??嫉降闹R(shí)點(diǎn),這篇文章為大家進(jìn)行了詳細(xì)介紹,需要的可以參考一下

一、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)文章

最新評(píng)論