闭包及其使用场景

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

什么是闭包

函数套函数,在内部函数 中访问外部函数中的变量 ,二者绑定在一起。即使外部函数已经执行完毕,内部函数也可以保持对外部函数执行上下文的引用,避免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;(必须的)

相关推荐
Michael.Scofield7 分钟前
vue: router基础用法
前端·javascript·vue.js
excel10 分钟前
webpack 模块 第 五 节
前端
excel18 分钟前
webpack 模块 第 四 节
前端
好_快28 分钟前
Lodash源码阅读-take
前端·javascript·源码阅读
好_快28 分钟前
Lodash源码阅读-takeRight
前端·javascript·源码阅读
好_快30 分钟前
Lodash源码阅读-takeRightWhile
前端·javascript·源码阅读
烂蜻蜓31 分钟前
在 HTML5 中使用 MathML 展示数学公式
前端·html·html5
好_快33 分钟前
Lodash源码阅读-takeWhile
前端·javascript·源码阅读
恋猫de小郭2 小时前
Android Studio Cloud 正式上线,不只是 Android,随时随地改 bug
android·前端·flutter
清岚_lxn6 小时前
原生SSE实现AI智能问答+Vue3前端打字机流效果
前端·javascript·人工智能·vue·ai问答