面试记录专刊:第16天

297 阅读2分钟

今日复习计划:内存泄漏

一. 前端知识

1. 内存泄漏

1.1 闭包变量泄漏
function fn() {
  let test = "test";

  return () => {
    console.log("hhhhh");
  };
}

let fn1 = fn();

fn1();

上诉代码不会造成内存泄漏,因为fn内的变量test并没有被引用

function fn() {
  let test = "test";

  return () => {
    console.log(test);
  };
}

let fn1 = fn();

fn1();

上诉代码中,test变量被引用,所以test不会被回收,造成内存泄漏。那如何正确使用呢?如下:

function fn() {
  let test = "test";
  return () => {
    console.log(test);
  };
}
let fn1 = fn();
fn1();
fn1 = null
1.2 隐式全局变量

不使用声明符进行变量声明,变量会变成全局变量,对于全局变量的回收特别困难,会造成内存泄漏。

function fn() {
  test = "111";
  console.log(test);
}
fn();

解决方案:正确使用变量声明或者使用严格模式。如果非要使用全局变量,应当正确关闭引用。

let global = {a:1}
xxxx

global = null
1.3 游离DOM引用
<div id="root">
  <ul id="ul">
    <li></li>
    <li></li>
    <li id="li3"></li>
    <li></li>
  </ul>
</div>
<script>
  let root = document.querySelector("#root");
  let ul = document.querySelector("#ul");
  let li3 = document.querySelector("#li3"); 
  // 由于ul变量存在,整个ul及其子元素都不能GC
  root.removeChild(ul) 
  // 虽置空了ul变量,但由于li3变量引用ul的子节点,所以ul元素依然不能被GC
  ul = null
  // 已无变量引用,此时可以GC
  li3 = null
</script>
1.4 定时器

setTimoutsetIntervalrequestAnimationFrame如不使用对应API正确关闭,则会导致回调函数里变量以及回调函数里的依赖没有正确回收。

let test = "1";
setTimeout(() => {
  let test2 = "2";
  console.log(test, test2);
}, 1000);

上述代码中,变量test1test2都不会被回收。使用clearTimeclearIntervalcancelAnimationFrame清除对应的定时器。

1.5 事件监听
function fn() {}
window.addEventListener("resize", fn);
window.removeEventListener("resize", fn);
1.6 引用型数据类型(map,object)

当使用 Map 或 Set 存储对象时,同 Object 一致都是强引用,如果不将其主动清除引用,其同样会造成内存不自动进行回收。

let obj = {name: 'cxm'}
obj = null  // {name: 'cxm'} 被正常回收

MapSet中的key如为引用型,因为是强引用,所以key值的引用型数据将永远不会被回收。应当使用weakMapweakSet替代。

let key = { name: "map" };
let map = new Map();
map.set(key, "test");
map.clear(); // key对象不会被回收

let weakMap = new WeakMap();
weakMap.set(key, "test");
weakMap.clear(); // weakMap的引用为弱引用,故key对象会被正常回收

二. 算法

// Given an object, then convert it to a new object structure
// For example:
const entry = {
    a: {
      b: {
        c: {
          dd: 'abcdd',
        },
      },
      d: {
        xx: 'adxx',
      },
      e: 'ae',
    },
  };
  // When we invoke the "convertObject" method and pass the parameter "entry" object: convertObject(entry)
  // The result is:
  // {
  //   'a.b.c.dd': 'abcdd',
  //   'a.d.xx': 'adxx',
  //   'a.e': 'ae',
  // };
  // Please complete the convertObject method
  function convertObject(o) {
    let res = {}
    for(let key in o){
      let item = o[key]
      console.log(Object.prototype.toString.call(item));
      if(Object.prototype.toString.call(item) === '[object Object]'){
         let tempObj = convertObject(item)
         for(let sKey in tempObj){
           let sTempObj = tempObj[sKey]
           let connectKey = `${key}.${sKey}`
           res[connectKey] = sTempObj
         }
      }else{
         res[key] = item
      }
    }
    
    return res
    
  }
  console.log(  convertObject(entry));

三. 面经

1. 不可变量

2. Promise的原理

3. React18

4. Redux原理

5. webpack Tree-shaking实现

总结:

今天是被虐的一天,准备不足。被问了一些知识盲区,感觉这些年其实光顾着写业务,一些底层的知识面还是很匮乏。

四. 其他

He said, one day you’ll leave this world behind,
so live a life you will remenber