[js - closure ]

109 阅读2分钟

闭包

如果一个函数用到了外部的变量,那么该函数和这个变量就叫做闭包。

闭包隐藏细节就是,生成一个变量,一个函数去读这个变量。

好处:

  1. 保存: 它不会被垃圾回收,因为那个变量没有离开执行环境
  2. 保护: 可以理解成私有变量,只有用函数才能读取它,用户永远不能直接操作变量,必须通过函数来操作变量。

缺陷:内存泄漏

function fn() {
  let a = 100;
  return function () {
    console.log(a);
  };
}

应用

简单的 cache 工具。

// 做一个简单的 cache 工具
function createCache() {
  let data = {};
  return {
    get: function (key) {
      return data[key];
    },
    set: function (key, value) {
      data[key] = value;
    },
  };
}
const c = createCache();
c.set("a", 100);
c.get("a");
// 在这一层访问不到 data,只能访问到 data 返回的数据

函数只执行一次

function wrapper(func) {
  let judge = true;
  return function (arg) {
    if (judge) {
      judge = false;
      func(arg);
    }
  };
}

let getResult = wrapper(console.log);
getResult(1);
getResult(1);
getResult(1);

防抖节流

// 防抖
// 触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间。
// 防抖重在清零 clearTimeout(timer)

// 生活场景:坐电梯,按上楼层号
// 登录、发短信等按钮避免用户点击太快
// 调整浏览器窗口大小时,resize 次数过于频繁
// 文本编辑器实时保存
function debounce(f, wait) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      f(...args);
    }, wait);
  };
}


// 节流
// 高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行效率。
// // 生活场景:玩游戏,发技能
// input 框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求 (也可做防抖)
// scroll 事件,查看城市
function throttle(f, wait) {
  let timer;
  return (...args) => {
    if (timer) {
      return;
    }
    timer = setTimeout(() => {
      f(...args);
      timer = null;
    }, wait);
  };
}

创建 10 个 a 标签,点击弹出结果

前置知识点:

es6 中 let、const 有块的概念,作用在 {} 内,变量不可以重复声明,会产生暂时性死区

let /const/function会把当前所在的大括号(除函数之外)作为一个全新的块级上下文,应用这个机制,在开发项目的时候,遇到循环事件绑定等类似的需求,无需再自己构建闭包来存储,只要基于let的块作用特征即可解决

// i 在外面
let i, a;
for (i = 0; i < 10; i++) {
    const a = document.createElement("a");
    a.innerHTML = i + "<br />";
    a.addEventListener("click", function (e) {
        e.preventDefault();
        alert(i);
    });
    document.body.appendChild(a);
}

// 这种情况不论点击哪一个数,都是弹出 10。


let a;
for (let i = 0; i < 10; i++) {
    const a = document.createElement("a");
    a.innerHTML = i + "<br />";
    a.addEventListener("click", function (e) {
        e.preventDefault();
        alert(i);
    });
    document.body.appendChild(a);
} 

// 这种情况不论点击哪一个数,都是弹出 相应 i。

其他 juejin.cn/post/705532…