JS基础04 -- 垃圾回收及闭包注意事项

155 阅读3分钟

垃圾回收

栈内存释放

函数上下文执行完成后,若不存在闭包区域,则释放 全局上下文只有在关闭页面、刷新页面时才会释放,(eg:vuex、redux的全局变量存储)

堆内存释放

  • IE方案:引用计数 可能会产生计数混乱从而泄露内存 让所有引用堆内存地址的变量全部赋值为null(或改指向),使得没有变量来引用它,此时这个堆内存就会被浏览器GC回收,从而释放堆内存
  • 谷歌方案:查找引用 浏览器会在空闲的时间段内,检查所有堆内存是否被引用,若没有被任何一个变量引用,则释放

闭包问题

闭包垃圾回收

let x = 10;
function fn(){
    function f(){}
    // fn执行时,fn内部的f会被外部的window引用,形成不销毁的EC(fn)
    window.f = f;
}
// fn执行产生一个EC(fn)且不被销毁
fn();
// 取消堆中函数的引用,fn指向的堆内存释放
fn = null;
// f仍然能正常执行,虽然fn存储在堆内存中的描述字符串被回收了所以不能fn()了,但EC(fn)还存在
f();
// f堆内存释放,EC(fn)栈内存不存在闭包区域,则栈内存释放
// window.f = null

闭包编程技巧

JS单例

高级单例模式,在全局下进存储命名空间,只要命名空间不冲突,则其内部属性方法一定不冲突。使用一块独立的空间进行某种活动称为单例(体现为一个大对象管理信息)。

// utils_a为一个单例
let utils_a = (function () {
  //此处的fn是AO(IIFEA)的私有变量
  let a = 0
  function fn () {}
  ……
  // 外部命名空间utils_a引用此处暴露的接口,可以供别的模块(命名空间)调用
  return {
    a,
    fn
  }
})()
// utils_b为一个单例
let utils_b = (function () {
  //此处的fn是AO(IIFEB)的私有变量
  function fn () {}
  ……
  return {
    fn
  }
})()

惰性函数

惰性函数不能具名(因为具名后,进行重定向操作时,找到的函数名不是全局的,而是具名的函数签名,这个签名不能更改导致重定向失败)

/**需求:
 * 获取元素的样式,使用window.getComputedStyle(element).属性名获取,但不兼容IE678
 * 若不兼容,则使用element.currentStyle(属性名)
 */
function get_css(element, attribute_name) {
  // 多次调用get_css会多次进行校验,实际需求只需一次校验即可
  if('getComputedStyle' in window) {
    return window.getComputedStyle(element)[attribute_name]
  }
  else {
    return element.currentStyle(attribute_name)
  }
}

//改造(不能写成let get_css = function get_css(){})
let get_css = function (element, attribute_name){
  // 得到校验结果直接重定向get_css,出第一次外的函数执行都将跳过校验
  if('getComputedStyle' in window) {
    // 重定向的function被全局变量get_css引用,则形成闭包
    get_css = function (element, attribute_name){
      return window.getComputedStyle(element)[attribute_name]
    }
  }else {
    get_css = function (element, attribute_name){
      return element.currentStyle(attribute_name)
    }
  }
  //第一次走了校验的函数执行也需要有结果
  return get_css(element, attribute_name)
}


// 使用
get_css(box, height);
get_css(box, width);

柯里化

核心思想是“预存储,预处理”。造成闭包在本级上下文中保存一些信息,供下级上下文调用。

// ==========================柯里化实现链式调用=========================
// curring返回的函数能实现链式调用求和
let curring = () => {
    // 此处的上下文空间可以作为add函数的闭包数据存储空间
    let storeArr = [];
    // 柯里化返回
    const add = (...args) => {
        storeArr = [...storeArr, ...args];
        return add;
    }
    // 不知道链式次数,最后返回的还是一个函数,当使用返回值时,意味着需要数组的求和值,
    // 可以控制add函数的转化输出来得到求和值
    add[Symbol.toPrimitive] = function () {
        return storeArr.reduce((total,next) => total + next);
    }
    return add;
}
let add1 = curring();
console.log(add(1)(2)(3))    // 6
let add2 = curring();
console.log(add(1, 2, 3)(4))    // 10

compose组合函数

    const add = x => x + 1;
    const minus = x => x - 1;
    const multiply = x => x * 2;
    // 组合函数,依次对操作对象handle执行用户指定的函数序列
    const compose = (...funcs) => {
      return function (handle) {
        return funcs.reduce((res, next) => {
          res = next(res);
          return res;
        }, handle)
      }
    }
    let op = compose(add, add, multiply, minus);
    debugger;
    let res = op(3)
    console.log(res)