闭包及其使用场景

主播最近刷了一遍八股文,复盘模拟面试的时候发现有很多问题只是大概明白有这么个事情但是不能用语言准确地描述出来。因此开了这个专栏,意在整理一些前端面试高频考点及回答策略。

什么是闭包

函数套函数,在内部函数 中访问外部函数中的变量 ,二者绑定在一起。即使外部函数已经执行完毕,内部函数也可以保持对外部函数执行上下文的引用,避免GC回收掉。

闭包有两个特点:

  • 创建私有变量
  • 延长变量的生命周期

闭包的形成有两个条件:

  • 闭包是在函数被调用执行的时候才被确认创建的
  • 闭包的形成与作用域链的访问顺序有直接关系,只有内部函数访问了上层作用域链的变量时才会形成闭包 举一个最简单的例子
js 复制代码
function outer() {
    let i = 0;
    function inner() {
        console.log(i);
    }
}

使用场景

封装私有变量

根据闭包的特点,可以利用闭包来封装私有变量,这样外部可以使用这个变量但无法修改。

js 复制代码
function creatCounter() {
    let count = 0;
    return {
        increment: () => ++count,
        getValue: () => count
    }
}

const counter = creatCounter();
console.log(counter.increment()); // 1

柯里化

闭包可以记住已经传入的参数,避免重复传相同参数,实现参数的复用

js 复制代码
function getArea(width) {
    return height => width * height;
}
const getTenWidthArea = getArea(10)
const aera1 = getTenWidthArea(5) // 50

既然提到了柯里化,就来寿司一下

柯里化是函数式编程里的概念,指的是固定多参数函数的一些参数,返回接收该函数剩余参数的一个新函数;如果参数总数达到要求,没有剩余参数,才调用。

一个经过柯里化的函数,参数可以分多次传入,并且可以在参数不足的情况下延迟执行。

js 复制代码
// 将一个函数柯里化
function curry(fn) {
    // 将函数已获得的参数个数与原函数所需参数个数进行比较
    return function curried(...args) {
        // 如果参数个数大于等于原函数的参数个数,则直接调用原函数
        if (args.length >= fn.length) {
            return fn.apply(this, args);
        } 
        // 否则,返回一个新函数,用于接收剩余的参数
        else {
            return function (...args2) {
                return curried.apply(this, args.concat(args2));
            }
        }
    }
}

var add = (a, b, c) => a + b + c
const curriedAdd = curry(add)

curriedAdd(1)(2)(3) // 6
curriedAdd(1, 2)(3) // 6
curriedAdd(1, 2, 3) // 6

防抖节流

防抖 :事件被触发n秒后再执行回调,如果期间又被触发则重新计时。类似打王者的回城操作

本质就是,连续触发时,最后一次操作后等待规定时间再执行。使用场景比如搜索联想时节约请求资源、频繁调整窗口大小时只计算最后一次的布局。

js 复制代码
// 参数是回调函数和要等待的时间间隔,返回经过防抖化的函数
function debounce(func, wait) {
    let timer = null // 闭包保存定时器
    return (...args) => {
        if (timer) clearTimeout(timer) // 清除之前的定时器
        timer = setTimeout(() => {
            func.apply(this, args)
        }, wait)
    }
}

节流 :规定时间内多次触发,只执行一次。类似射击游戏控制射击频率。

有两种版本,分别是c触发后立即执行和延迟n秒再执行。应用场景比如监听滚动事件加载更多、鼠标单位时间内多次点击。

js 复制代码
// 定时器版本,延迟执行
function throttle(func, wait) {
    let timer = null // 闭包保存定时器
    return (...args) => {
        if (timer) return // 如果有定时器,不再执行直接返回
        timer = setTimeout(() => { // 无定时器创建定时器
            func.apply(this, args)
            timer = null // 执行后重置定时器
        }, wait)
    }
}

// 时间戳版本,立即执行
function throttle(func, wait) {
    let prev = 0 // 闭包保存上一次执行时间
    return (...args) => {
        let now = Date.now()
        if (now - prev >= wait) { // 如果超过时间间隔立即执行
            func.apply(this, args)
            prev = now // 执行后重置上一次执行时间
        }
    }
}

副作用

闭包在处理速度和内存消耗方面对脚本性能有负面影响。可能导致内存泄漏,长期引用外部变量可能导致内存无法释放,需要手动解除引用。

可以在闭包内部设置手动释放闭包资源的release方法,在结束对变量的引用后调用closure.release()(不是必须的,如果未暴露内部变量就不用操作这一步),并显式解除闭包本身的引用closure = null;(必须的)

相关推荐
zach11 小时前
前端落地实战:Next.js 项目 Nginx + PM2 生产部署全流程(附踩坑总结)
前端·pm2·next.js
overmind11 小时前
oeasy教h5前端001 修改的乐趣_直接修改在线网页
前端
只睡四小时11 小时前
JS 手写 D-pad 空间导航:电视端焦点引擎实战
android·开发语言·前端·javascript·ecmascript·hls·空间导航
恋猫de小郭11 小时前
Android 原生的 Compose A2UI 也来了,你还抱着 XML 养老吗?
android·前端·flutter
庄园特聘拆椅狂魔11 小时前
从连连看到粒子消散——前端小游戏与视觉特效的技术选型
前端
JudithHuang12 小时前
React 常用 Hooks
前端·react.js·前端框架
sycmancia13 小时前
Qml——Window元素使用
前端
每天都好困13 小时前
03 AR 投影与误差修正:把地图上的点贴到视频画面上
前端
每天都好困13 小时前
04 地图图层封装:像管理 Photoshop 图层一样管理地图元素
前端
去伪存真13 小时前
构建你的第一个 DevOps AI Agent:自动化捕获、分析并修复 CI 故障
前端·agent