闭包
如果一个函数用到了外部的变量,那么该函数和这个变量就叫做闭包。
闭包隐藏细节就是,生成一个变量,一个函数去读这个变量。
好处:
- 保存: 它不会被垃圾回收,因为那个变量没有离开执行环境
- 保护: 可以理解成私有变量,只有用函数才能读取它,用户永远不能直接操作变量,必须通过函数来操作变量。
缺陷:内存泄漏
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。