久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長(zhǎng)資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      32個(gè)純手寫JS,鞏固你的JS基礎(chǔ)

      javascript欄目為大家介紹32個(gè)純手寫JS,鞏固JS基礎(chǔ)(面試高頻),一起學(xué)習(xí)吧。

      32個(gè)純手寫JS,鞏固你的JS基礎(chǔ)

      作為前端開(kāi)發(fā),JS是重中之重,最近結(jié)束了面試的高峰期,基本上offer也定下來(lái)了就等開(kāi)獎(jiǎng),趁著這個(gè)時(shí)間總結(jié)下32個(gè)手寫JS問(wèn)題,這些都是高頻面試題,希望對(duì)你能有所幫助。

      關(guān)于源碼都緊遵規(guī)范,都可跑通MDN示例,其余的大多會(huì)涉及一些關(guān)于JS的應(yīng)用題和本人面試過(guò)程

      01.數(shù)組扁平化

      數(shù)組扁平化是指將一個(gè)多維數(shù)組變?yōu)橐粋€(gè)一維數(shù)組

      const arr = [1, [2, [3, [4, 5]]], 6];// => [1, 2, 3, 4, 5, 6]復(fù)制代碼

      方法一:使用flat()

      const res1 = arr.flat(Infinity);復(fù)制代碼

      方法二:利用正則

      const res2 = JSON.stringify(arr).replace(/[|]/g, '').split(',');復(fù)制代碼

      但數(shù)據(jù)類型都會(huì)變?yōu)樽址?/p>

      方法三:正則改良版本

      const res3 = JSON.parse('[' + JSON.stringify(arr).replace(/[|]/g, '') + ']');復(fù)制代碼

      方法四:使用reduce

      const flatten = arr => {  return arr.reduce((pre, cur) => {    return pre.concat(Array.isArray(cur) ? flatten(cur) : cur);   }, []) }const res4 = flatten(arr);復(fù)制代碼

      方法五:函數(shù)遞歸

      const res5 = [];const fn = arr => {  for (let i = 0; i < arr.length; i++) {    if (Array.isArray(arr[i])) {       fn(arr[i]);     } else {       res5.push(arr[i]);     }   } } fn(arr);復(fù)制代碼

      02.數(shù)組去重

      const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];// => [1, '1', 17, true, false, 'true', 'a', {}, {}]復(fù)制代碼

      方法一:利用Set

      const res1 = Array.from(new Set(arr));復(fù)制代碼

      方法二:兩層for循環(huán)+splice

      const unique1 = arr => {  let len = arr.length;  for (let i = 0; i < len; i++) {    for (let j = i + 1; j < len; j++) {      if (arr[i] === arr[j]) {         arr.splice(j, 1);        // 每刪除一個(gè)樹(shù),j--保證j的值經(jīng)過(guò)自加后不變。同時(shí),len--,減少循環(huán)次數(shù)提升性能         len--;         j--;       }     }   }  return arr; }復(fù)制代碼

      方法三:利用indexOf

      const unique2 = arr => {  const res = [];  for (let i = 0; i < arr.length; i++) {    if (res.indexOf(arr[i]) === -1) res.push(arr[i]);   }  return res; }復(fù)制代碼

      當(dāng)然也可以用include、filter,思路大同小異。

      方法四:利用include

      const unique3 = arr => {  const res = [];  for (let i = 0; i < arr.length; i++) {    if (!res.includes(arr[i])) res.push(arr[i]);   }  return res; }復(fù)制代碼

      方法五:利用filter

      const unique4 = arr => {  return arr.filter((item, index) => {    return arr.indexOf(item) === index;   }); }復(fù)制代碼

      方法六:利用Map

      const unique5 = arr => {  const map = new Map();  const res = [];  for (let i = 0; i < arr.length; i++) {    if (!map.has(arr[i])) {       map.set(arr[i], true)       res.push(arr[i]);     }   }  return res; }復(fù)制代碼

      03.類數(shù)組轉(zhuǎn)化為數(shù)組

      類數(shù)組是具有length屬性,但不具有數(shù)組原型上的方法。常見(jiàn)的類數(shù)組有arguments、DOM操作方法返回的結(jié)果。

      方法一:Array.from

      Array.from(document.querySelectorAll('p'))復(fù)制代碼

      方法二:Array.prototype.slice.call()

      Array.prototype.slice.call(document.querySelectorAll('p'))復(fù)制代碼

      方法三:擴(kuò)展運(yùn)算符

      [...document.querySelectorAll('p')]復(fù)制代碼

      方法四:利用concat

      Array.prototype.concat.apply([], document.querySelectorAll('p'));復(fù)制代碼

      04.Array.prototype.filter()

      32個(gè)純手寫JS,鞏固你的JS基礎(chǔ)
      Array.prototype.filter = function(callback, thisArg) {  if (this == undefined) {    throw new TypeError('this is null or not undefined');   }  if (typeof callback !== 'function') {    throw new TypeError(callback + 'is not a function');   }  const res = [];  // 讓O成為回調(diào)函數(shù)的對(duì)象傳遞(強(qiáng)制轉(zhuǎn)換對(duì)象)   const O = Object(this);  // >>>0 保證len為number,且為正整數(shù)   const len = O.length >>> 0;  for (let i = 0; i < len; i++) {    // 檢查i是否在O的屬性(會(huì)檢查原型鏈)     if (i in O) {      // 回調(diào)函數(shù)調(diào)用傳參       if (callback.call(thisArg, O[i], i, O)) {         res.push(O[i]);       }     }   }  return res; }復(fù)制代碼

      對(duì)于>>>0有疑問(wèn)的:解釋>>>0的作用

      05.Array.prototype.map()

      32個(gè)純手寫JS,鞏固你的JS基礎(chǔ)
      Array.prototype.map = function(callback, thisArg) {  if (this == undefined) {    throw new TypeError('this is null or not defined');   }  if (typeof callback !== 'function') {    throw new TypeError(callback + ' is not a function');   }  const res = [];  // 同理   const O = Object(this);  const len = O.length >>> 0;  for (let i = 0; i < len; i++) {    if (i in O) {      // 調(diào)用回調(diào)函數(shù)并傳入新數(shù)組       res[i] = callback.call(thisArg, O[i], i, this);     }   }  return res; }復(fù)制代碼

      06.Array.prototype.forEach()

      32個(gè)純手寫JS,鞏固你的JS基礎(chǔ)

      forEach跟map類似,唯一不同的是forEach是沒(méi)有返回值的。

      Array.prototype.forEach = function(callback, thisArg) {  if (this == null) {    throw new TypeError('this is null or not defined');   }  if (typeof callback !== "function") {    throw new TypeError(callback + ' is not a function');   }  const O = Object(this);  const len = O.length >>> 0;  let k = 0;  while (k < len) {    if (k in O) {       callback.call(thisArg, O[k], k, O);     }     k++;   } }復(fù)制代碼

      07.Array.prototype.reduce()

      32個(gè)純手寫JS,鞏固你的JS基礎(chǔ)
      Array.prototype.reduce = function(callback, initialValue) {  if (this == undefined) {    throw new TypeError('this is null or not defined');   }  if (typeof callback !== 'function') {    throw new TypeError(callbackfn + ' is not a function');   }  const O = Object(this);  const len = this.length >>> 0;  let accumulator = initialValue;  let k = 0;  // 如果第二個(gè)參數(shù)為undefined的情況下   // 則數(shù)組的第一個(gè)有效值作為累加器的初始值   if (accumulator === undefined) {    while (k < len && !(k in O)) {       k++;     }    // 如果超出數(shù)組界限還沒(méi)有找到累加器的初始值,則TypeError     if (k >= len) {      throw new TypeError('Reduce of empty array with no initial value');     }     accumulator = O[k++];   }  while (k < len) {    if (k in O) {       accumulator = callback.call(undefined, accumulator, O[k], k, O);     }     k++;   }  return accumulator; }復(fù)制代碼

      08.Function.prototype.apply()

      第一個(gè)參數(shù)是綁定的this,默認(rèn)為window,第二個(gè)參數(shù)是數(shù)組或類數(shù)組

      Function.prototype.apply = function(context = window, args) {  if (typeof this !== 'function') {    throw new TypeError('Type Error');   }  const fn = Symbol('fn');   context[fn] = this;  const res = context[fn](...args);  delete context[fn];  return res; }復(fù)制代碼

      09.Function.prototype.call

      call唯一不同的是,call()方法接受的是一個(gè)參數(shù)列表

      Function.prototype.call = function(context = window, ...args) {  if (typeof this !== 'function') {    throw new TypeError('Type Error');   }  const fn = Symbol('fn');   context[fn] = this;  const res = context[fn](...args);  delete context[fn];  return res; }復(fù)制代碼

      10.Function.prototype.bind

      Function.prototype.bind = function(context, ...args) {  if (typeof this !== 'function') {    throw new Error("Type Error");   }  // 保存this的值   var self = this;  return function F() {    // 考慮new的情況     if(this instanceof F) {      return new self(...args, ...arguments)     }    return self.apply(context, [...args, ...arguments])   } }復(fù)制代碼

      11.debounce(防抖)

      觸發(fā)高頻時(shí)間后n秒內(nèi)函數(shù)只會(huì)執(zhí)行一次,如果n秒內(nèi)高頻時(shí)間再次觸發(fā),則重新計(jì)算時(shí)間。

      const debounce = (fn, time) => {  let timeout = null;  return function() {     clearTimeout(timeout)     timeout = setTimeout(() => {       fn.apply(this, arguments);     }, time);   } };復(fù)制代碼

      防抖常應(yīng)用于用戶進(jìn)行搜索輸入節(jié)約請(qǐng)求資源,window觸發(fā)resize事件時(shí)進(jìn)行防抖只觸發(fā)一次。

      12.throttle(節(jié)流)

      高頻時(shí)間觸發(fā),但n秒內(nèi)只會(huì)執(zhí)行一次,所以節(jié)流會(huì)稀釋函數(shù)的執(zhí)行頻率。

      const throttle = (fn, time) => {  let flag = true;  return function() {    if (!flag) return;     flag = false;     setTimeout(() => {       fn.apply(this, arguments);       flag = true;     }, time);   } }復(fù)制代碼

      節(jié)流常應(yīng)用于鼠標(biāo)不斷點(diǎn)擊觸發(fā)、監(jiān)聽(tīng)滾動(dòng)事件。

      13.函數(shù)珂里化

      指的是將一個(gè)接受多個(gè)參數(shù)的函數(shù) 變?yōu)?接受一個(gè)參數(shù)返回一個(gè)函數(shù)的固定形式,這樣便于再次調(diào)用,例如f(1)(2)

      經(jīng)典面試題:實(shí)現(xiàn)add(1)(2)(3)(4)=10; 、 add(1)(1,2,3)(2)=9;

      function add() {  const _args = [...arguments];  function fn() {     _args.push(...arguments);    return fn;   }   fn.toString = function() {    return _args.reduce((sum, cur) => sum + cur);   }  return fn; }復(fù)制代碼

      14.模擬new操作

      3個(gè)步驟:

      1. ctor.prototype為原型創(chuàng)建一個(gè)對(duì)象。
      2. 執(zhí)行構(gòu)造函數(shù)并將this綁定到新創(chuàng)建的對(duì)象上。
      3. 判斷構(gòu)造函數(shù)執(zhí)行返回的結(jié)果是否是引用數(shù)據(jù)類型,若是則返回構(gòu)造函數(shù)執(zhí)行的結(jié)果,否則返回創(chuàng)建的對(duì)象。
      function newOperator(ctor, ...args) {  if (typeof ctor !== 'function') {    throw new TypeError('Type Error');   }  const obj = Object.create(ctor.prototype);  const res = ctor.apply(obj, args);  const isObject = typeof res === 'object' && res !== null;  const isFunction = typeof res === 'function';  return isObject || isFunction ? res : obj; }復(fù)制代碼

      15.instanceof

      instanceof運(yùn)算符用于檢測(cè)構(gòu)造函數(shù)的prototype屬性是否出現(xiàn)在某個(gè)實(shí)例對(duì)象的原型鏈上。

      const myInstanceof = (left, right) => {  // 基本數(shù)據(jù)類型都返回false   if (typeof left !== 'object' || left === null) return false;  let proto = Object.getPrototypeOf(left);  while (true) {    if (proto === null) return false;    if (proto === right.prototype) return true;     proto = Object.getPrototypeOf(proto);   } }復(fù)制代碼

      16.原型繼承

      這里只寫寄生組合繼承了,中間還有幾個(gè)演變過(guò)來(lái)的繼承但都有一些缺陷

      function Parent() {  this.name = 'parent'; }function Child() {   Parent.call(this);  this.type = 'children'; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child;復(fù)制代碼

      17.Object.is

      Object.is解決的主要是這兩個(gè)問(wèn)題:

      +0 === -0  // true NaN === NaN // false復(fù)制代碼
      const is= (x, y) => {  if (x === y) {    // +0和-0應(yīng)該不相等     return x !== 0 || y !== 0 || 1/x === 1/y;   } else {    return x !== x && y !== y;   } }復(fù)制代碼

      18.Object.assign

      Object.assign()方法用于將所有可枚舉屬性的值從一個(gè)或多個(gè)源對(duì)象復(fù)制到目標(biāo)對(duì)象。它將返回目標(biāo)對(duì)象(請(qǐng)注意這個(gè)操作是淺拷貝)

      Object.defineProperty(Object, 'assign', {  value: function(target, ...args) {    if (target == null) {      return new TypeError('Cannot convert undefined or null to object');     }         // 目標(biāo)對(duì)象需要統(tǒng)一是引用數(shù)據(jù)類型,若不是會(huì)自動(dòng)轉(zhuǎn)換     const to = Object(target);    for (let i = 0; i < args.length; i++) {      // 每一個(gè)源對(duì)象       const nextSource = args[i];      if (nextSource !== null) {        // 使用for...in和hasOwnProperty雙重判斷,確保只拿到本身的屬性、方法(不包含繼承的)         for (const nextKey in nextSource) {          if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {             to[nextKey] = nextSource[nextKey];           }         }       }     }    return to;   },  // 不可枚舉   enumerable: false,  writable: true,  configurable: true, })復(fù)制代碼

      19.深拷貝

      遞歸的完整版本(考慮到了Symbol屬性):

      const cloneDeep1 = (target, hash = new WeakMap()) => {  // 對(duì)于傳入?yún)?shù)處理   if (typeof target !== 'object' || target === null) {    return target;   }  // 哈希表中存在直接返回   if (hash.has(target)) return hash.get(target);  const cloneTarget = Array.isArray(target) ? [] : {};   hash.set(target, cloneTarget);  // 針對(duì)Symbol屬性   const symKeys = Object.getOwnPropertySymbols(target);  if (symKeys.length) {     symKeys.forEach(symKey => {      if (typeof target[symKey] === 'object' && target[symKey] !== null) {         cloneTarget[symKey] = cloneDeep1(target[symKey]);       } else {         cloneTarget[symKey] = target[symKey];       }     })   }  for (const i in target) {    if (Object.prototype.hasOwnProperty.call(target, i)) {       cloneTarget[i] =        typeof target[i] === 'object' && target[i] !== null         ? cloneDeep1(target[i], hash)         : target[i];     }   }  return cloneTarget; }復(fù)制代碼

      20.Promise

      實(shí)現(xiàn)思路:Promise源碼實(shí)現(xiàn)

      const PENDING = 'PENDING';      // 進(jìn)行中const FULFILLED = 'FULFILLED';  // 已成功const REJECTED = 'REJECTED';    // 已失敗class Promise {  constructor(exector) {    // 初始化狀態(tài)     this.status = PENDING;    // 將成功、失敗結(jié)果放在this上,便于then、catch訪問(wèn)     this.value = undefined;    this.reason = undefined;    // 成功態(tài)回調(diào)函數(shù)隊(duì)列     this.onFulfilledCallbacks = [];    // 失敗態(tài)回調(diào)函數(shù)隊(duì)列     this.onRejectedCallbacks = [];    const resolve = value => {      // 只有進(jìn)行中狀態(tài)才能更改狀態(tài)       if (this.status === PENDING) {        this.status = FULFILLED;        this.value = value;        // 成功態(tài)函數(shù)依次執(zhí)行         this.onFulfilledCallbacks.forEach(fn => fn(this.value));       }     }    const reject = reason => {      // 只有進(jìn)行中狀態(tài)才能更改狀態(tài)       if (this.status === PENDING) {        this.status = REJECTED;        this.reason = reason;        // 失敗態(tài)函數(shù)依次執(zhí)行         this.onRejectedCallbacks.forEach(fn => fn(this.reason))       }     }    try {      // 立即執(zhí)行executor       // 把內(nèi)部的resolve和reject傳入executor,用戶可調(diào)用resolve和reject       exector(resolve, reject);     } catch(e) {      // executor執(zhí)行出錯(cuò),將錯(cuò)誤內(nèi)容reject拋出去       reject(e);     }   }   then(onFulfilled, onRejected) {     onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;     onRejected = typeof onRejected === 'function'? onRejected:      reason => { throw new Error(reason instanceof Error ? reason.message:reason) }    // 保存this     const self = this;    return new Promise((resolve, reject) => {      if (self.status === PENDING) {         self.onFulfilledCallbacks.push(() => {          // try捕獲錯(cuò)誤           try {            // 模擬微任務(wù)             setTimeout(() => {              const result = onFulfilled(self.value);              // 分兩種情況:               // 1. 回調(diào)函數(shù)返回值是Promise,執(zhí)行then操作               // 2. 如果不是Promise,調(diào)用新Promise的resolve函數(shù)               result instanceof Promise ? result.then(resolve, reject) : resolve(result);             })           } catch(e) {             reject(e);           }         });         self.onRejectedCallbacks.push(() => {          // 以下同理           try {             setTimeout(() => {              const result = onRejected(self.reason);              // 不同點(diǎn):此時(shí)是reject               result instanceof Promise ? result.then(resolve, reject) : reject(result);             })           } catch(e) {             reject(e);           }         })       } else if (self.status === FULFILLED) {        try {           setTimeout(() => {            const result = onFulfilled(self.value);             result instanceof Promise ? result.then(resolve, reject) : resolve(result);           });         } catch(e) {           reject(e);         }       } else if (self.status === REJECTED){        try {           setTimeout(() => {            const result = onRejected(self.reason);             result instanceof Promise ? result.then(resolve, reject) : reject(result);           })         } catch(e) {           reject(e);         }       }     });   }  catch(onRejected) {    return this.then(null, onRejected);   }  static resolve(value) {    if (value instanceof Promise) {      // 如果是Promise實(shí)例,直接返回       return value;     } else {      // 如果不是Promise實(shí)例,返回一個(gè)新的Promise對(duì)象,狀態(tài)為FULFILLED       return new Promise((resolve, reject) => resolve(value));     }   }  static reject(reason) {    return new Promise((resolve, reject) => {       reject(reason);     })   } }復(fù)制代碼

      21.Promise.all

      Promise.all是支持鏈?zhǔn)秸{(diào)用的,本質(zhì)上就是返回了一個(gè)Promise實(shí)例,通過(guò)resolvereject來(lái)改變實(shí)例狀態(tài)。

      Promise.myAll = function(promiseArr) {  return new Promise((resolve, reject) => {    const ans = [];    let index = 0;    for (let i = 0; i < promiseArr.length; i++) {       promiseArr[i]       .then(res => {         ans[i] = res;         index++;        if (index === promiseArr.length) {           resolve(ans);         }       })       .catch(err => reject(err));     }   }) }復(fù)制代碼

      22.Promise.race

      Promise.race = function(promiseArr) {  return new Promise((resolve, reject) => {     promiseArr.forEach(p => {      // 如果不是Promise實(shí)例需要轉(zhuǎn)化為Promise實(shí)例       Promise.resolve(p).then(        val => resolve(val),         err => reject(err),       )     })   }) }復(fù)制代碼

      23.Promise并行限制

      就是實(shí)現(xiàn)有并行限制的Promise調(diào)度器問(wèn)題。

      詳細(xì)實(shí)現(xiàn)思路:某條高頻面試原題:實(shí)現(xiàn)有并行限制的Promise調(diào)度器

      class Scheduler {  constructor() {    this.queue = [];    this.maxCount = 2;    this.runCounts = 0;   }   add(promiseCreator) {    this.queue.push(promiseCreator);   }   taskStart() {    for (let i = 0; i < this.maxCount; i++) {      this.request();     }   }   request() {    if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {      return;     }    this.runCounts++;    this.queue.shift()().then(() => {      this.runCounts--;      this.request();     });   } }    const timeout = time => new Promise(resolve => {   setTimeout(resolve, time); })   const scheduler = new Scheduler();   const addTask = (time,order) => {   scheduler.add(() => timeout(time).then(()=>console.log(order))) }       addTask(1000, '1'); addTask(500, '2'); addTask(300, '3'); addTask(400, '4'); scheduler.taskStart()// 2// 3// 1// 4復(fù)制代碼

      24.JSONP

      script標(biāo)簽不遵循同源協(xié)議,可以用來(lái)進(jìn)行跨域請(qǐng)求,優(yōu)點(diǎn)就是兼容性好但僅限于GET請(qǐng)求

      const jsonp = ({ url, params, callbackName }) => {  const generateUrl = () => {    let dataSrc = '';    for (let key in params) {      if (Object.prototype.hasOwnProperty.call(params, key)) {         dataSrc += `${key}=${params[key]}&`;       }     }     dataSrc += `callback=${callbackName}`;    return `${url}?${dataSrc}`;   }  return new Promise((resolve, reject) => {    const scriptEle = document.createElement('script');     scriptEle.src = generateUrl();    document.body.appendChild(scriptEle);    window[callbackName] = data => {       resolve(data);      document.removeChild(scriptEle);     }   }) }復(fù)制代碼

      25.AJAX

      const getJSON = function(url) {  return new Promise((resolve, reject) => {    const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp');     xhr.open('GET', url, false);     xhr.setRequestHeader('Accept', 'application/json');     xhr.onreadystatechange = function() {      if (xhr.readyState !== 4) return;      if (xhr.status === 200 || xhr.status === 304) {         resolve(xhr.responseText);       } else {         reject(new Error(xhr.responseText));       }     }     xhr.send();   }) }復(fù)制代碼

      26.event模塊

      實(shí)現(xiàn)node中回調(diào)函數(shù)的機(jī)制,node中回調(diào)函數(shù)其實(shí)是內(nèi)部使用了觀察者模式

      觀察者模式:定義了對(duì)象間一種一對(duì)多的依賴關(guān)系,當(dāng)目標(biāo)對(duì)象Subject發(fā)生改變時(shí),所有依賴它的對(duì)象Observer都會(huì)得到通知。

      function EventEmitter() {  this.events = new Map(); }// 需要實(shí)現(xiàn)的一些方法:// addListener、removeListener、once、removeAllListeners、emit// 模擬實(shí)現(xiàn)addlistener方法const wrapCallback = (fn, once = false) => ({ callback: fn, once }); EventEmitter.prototype.addListener = function(type, fn, once = false) {  const hanlder = this.events.get(type);  if (!hanlder) {    // 沒(méi)有type綁定事件     this.events.set(type, wrapCallback(fn, once));   } else if (hanlder && typeof hanlder.callback === 'function') {    // 目前type事件只有一個(gè)回調(diào)     this.events.set(type, [hanlder, wrapCallback(fn, once)]);   } else {    // 目前type事件數(shù)>=2     hanlder.push(wrapCallback(fn, once));   } }// 模擬實(shí)現(xiàn)removeListenerEventEmitter.prototype.removeListener = function(type, listener) {  const hanlder = this.events.get(type);  if (!hanlder) return;  if (!Array.isArray(this.events)) {    if (hanlder.callback === listener.callback) this.events.delete(type);    else return;   }  for (let i = 0; i < hanlder.length; i++) {    const item = hanlder[i];    if (item.callback === listener.callback) {       hanlder.splice(i, 1);       i--;      if (hanlder.length === 1) {        this.events.set(type, hanlder[0]);       }     }   } }// 模擬實(shí)現(xiàn)once方法EventEmitter.prototype.once = function(type, listener) {  this.addListener(type, listener, true); }// 模擬實(shí)現(xiàn)emit方法EventEmitter.prototype.emit = function(type, ...args) {  const hanlder = this.events.get(type);  if (!hanlder) return;  if (Array.isArray(hanlder)) {     hanlder.forEach(item => {       item.callback.apply(this, args);      if (item.once) {        this.removeListener(type, item);       }     })   } else {     hanlder.callback.apply(this, args);    if (hanlder.once) {      this.events.delete(type);     }   }  return true; } EventEmitter.prototype.removeAllListeners = function(type) {  const hanlder = this.events.get(type);  if (!hanlder) return;  this.events.delete(type); }復(fù)制代碼

      27.圖片懶加載

      可以給img標(biāo)簽統(tǒng)一自定義屬性src='default.png',當(dāng)檢測(cè)到圖片出現(xiàn)在窗口之后再補(bǔ)充src屬性,此時(shí)才會(huì)進(jìn)行圖片資源加載。

      function lazyload() {  const imgs = document.getElementsByTagName('img');  const len = imgs.length;  // 視口的高度   const viewHeight = document.documentElement.clientHeight;  // 滾動(dòng)條高度   const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop;  for (let i = 0; i < len; i++) {    const offsetHeight = imgs[i].offsetTop;    if (offsetHeight < viewHeight + scrollHeight) {      const src = imgs[i].dataset.src;       imgs[i].src = src;     }   } }// 可以使用節(jié)流優(yōu)化一下window.addEventListener('scroll', lazyload);復(fù)制代碼

      28.滾動(dòng)加載

      原理就是監(jiān)聽(tīng)頁(yè)面滾動(dòng)事件,分析clientHeightscrollTop、scrollHeight三者的屬性關(guān)系。

      window.addEventListener('scroll', function() {  const clientHeight = document.documentElement.clientHeight;  const scrollTop = document.documentElement.scrollTop;  const scrollHeight = document.documentElement.scrollHeight;  if (clientHeight + scrollTop >= scrollHeight) {    // 檢測(cè)到滾動(dòng)至頁(yè)面底部,進(jìn)行后續(xù)操作     // ...   } }, false);復(fù)制代碼

      一個(gè)Demo:頁(yè)面滾動(dòng)加載的Demo

      29.渲染幾萬(wàn)條數(shù)據(jù)不卡住頁(yè)面

      渲染大數(shù)據(jù)時(shí),合理使用createDocumentFragmentrequestAnimationFrame,將操作切分為一小段一小段執(zhí)行。

      setTimeout(() => {  // 插入十萬(wàn)條數(shù)據(jù)   const total = 100000;  // 一次插入的數(shù)據(jù)   const once = 20;  // 插入數(shù)據(jù)需要的次數(shù)   const loopCount = Math.ceil(total / once);  let countOfRender = 0;  const ul = document.querySelector('ul');  // 添加數(shù)據(jù)的方法   function add() {    const fragment = document.createDocumentFragment();    for(let i = 0; i < once; i++) {      const li = document.createElement('li');       li.innerText = Math.floor(Math.random() * total);       fragment.appendChild(li);     }     ul.appendChild(fragment);     countOfRender += 1;     loop();   }  function loop() {    if(countOfRender < loopCount) {      window.requestAnimationFrame(add);     }   }   loop(); }, 0)復(fù)制代碼

      30.打印出當(dāng)前網(wǎng)頁(yè)使用了多少種HTML元素

      一行代碼可以解決:

      const fn = () => {  return [...new Set([...document.querySelectorAll('*')].map(el => el.tagName))].length; }復(fù)制代碼

      值得注意的是:DOM操作返回的是類數(shù)組,需要轉(zhuǎn)換為數(shù)組之后才可以調(diào)用數(shù)組的方法。

      31.將VirtualDom轉(zhuǎn)化為真實(shí)DOM結(jié)構(gòu)

      這是當(dāng)前SPA應(yīng)用的核心概念之一

      // vnode結(jié)構(gòu):// {//   tag,//   attrs,//   children,// }//Virtual DOM => DOMfunction render(vnode, container) {   container.appendChild(_render(vnode)); }function _render(vnode) {  // 如果是數(shù)字類型轉(zhuǎn)化為字符串   if (typeof vnode === 'number') {     vnode = String(vnode);   }  // 字符串類型直接就是文本節(jié)點(diǎn)   if (typeof vnode === 'string') {    return document.createTextNode(vnode);   }  // 普通DOM   const dom = document.createElement(vnode.tag);  if (vnode.attrs) {    // 遍歷屬性     Object.keys(vnode.attrs).forEach(key => {      const value = vnode.attrs[key];       dom.setAttribute(key, value);     })   }  // 子數(shù)組進(jìn)行遞歸操作   vnode.children.forEach(child => render(child, dom));  return dom; }復(fù)制代碼

      32.字符串解析問(wèn)題

      var a = {    b: 123,    c: '456',    e: '789', }var str=`a{a.b}aa{a.c}aa {a.d}aaaa`;// => 'a123aa456aa {a.d}aaaa'復(fù)制代碼

      實(shí)現(xiàn)函數(shù)使得將str字符串中的{}內(nèi)的變量替換,如果屬性不存在保持原樣(比如{a.d}

      類似于模版字符串,但有一點(diǎn)出入,實(shí)際上原理大差不差

      const fn1 = (str, obj) => {    let res = '';    // 標(biāo)志位,標(biāo)志前面是否有{     let flag = false;    let start;    for (let i = 0; i < str.length; i++) {        if (str[i] === '{') {             flag = true;             start = i + 1;            continue;         }        if (!flag) res += str[i];        else {            if (str[i] === '}') {                 flag = false;                 res += match(str.slice(start, i), obj);             }         }     }    return res; }// 對(duì)象匹配操作const match = (str, obj) => {    const keys = str.split('.').slice(1);    let index = 0;    let o = obj;    while (index < keys.length) {        const key = keys[index];        if (!o[key]) {            return `{${str}}`;         } else {             o = o[key];         }         index++;     }    return o; }復(fù)制代碼

      相關(guān)免費(fèi)學(xué)習(xí)推薦:javascript(視頻)

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號(hào)-2    滬公網(wǎng)安備31011702889846號(hào)