「通俗读物」- 闭包

前言

大家好,我是珂圩

闭包是一种特殊函数,但是对于闭包的负面新闻也是有不少。

为什么闭包那么容易招黑呢?

本篇文章就带你了解一下闭包的本质以及合理的使用闭包

什么是闭包

闭包:也就是让你可以在一个内层函数中访问到外层函数的作用域 在javaScript中,每创建一个函数,闭包就会在函数创建的同时被创建出来,作为函数内部与外部连接起来的一座桥梁

js 复制代码
    function init() {
        var name = 'Mozilla'  //name 是一个被init创建的局部变量
        function dispalyName (){  //displayName() 是内部函数,一个闭包
            alert(name)   //使用了父函数中声明的函数
        }
        dispalyName()
    }
    init()

可能有些朋友比较对于闭包定义还是比较模糊,用一句通俗的话来说就是可以访问外部函数的变量

来几个例子看一下

闭包的其中一种形式,在displayName函数中没有使用name变量,但是dispalyName中是可以访问到的

注:不一定要使用外部函数的变量才叫闭包,而是可以访问到外部函数就可以成为闭包函数

js 复制代码
    function init() {
        var name = 'Mozilla' 
        function dispalyName (){}
        dispalyName()
    }
    init()

下面这种函数也可以称作为闭包,虽然函数dispalyName没有在init中定义,但是通过穿参的方式displayName也可以获取到init的内部变量。

js 复制代码
    function init() {
        var name = 'Mozilla' 
        dispalyName(name)
    }
    function dispalyName (name){}
    init()

使用场景

任何闭包的使用场景都离不开这两点

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

一般函数的词法环境在函数返回就会被销毁,但是闭包函数会保存对创建时所在词法环境的引用,即便创建时所在的执行上下文被销毁,但创建时所在词法环境依然存在,以达到延长变量的声明周期的目的

词法环境:每当创建一个函数时,都会创建一个新的词法环境。这个词法环境会记录函数执行时所处的作用域以及函数中定义的变量和函数。当函数执行完成后,它的词法环境会被销毁。但是闭包是一个特殊情况。

柯里化函数

柯里化的目的在于避免频繁调用具有享用参数函数的同时,又能够轻松的重用

js 复制代码
    function addition (a,b){
        return a + b
    }
    // 其中某一个参数 经常会一样
    addition(1,2)
    addition(1,3)
    addition(1,4)
    
    // 我们可以使用闭包柯里化计算
   function aadition (a){
       return b =>{
           return b
       }
   }
   let aAddition = addition(1)
   bAddition(2)
   bAddition(3)
   bAddition(4)
   //这样就可以 轻松复用 

闭包模拟私有方法

js 复制代码
    var fun = function (){
        var a = 0
        
        function change(val){
            a += val
        }
        return {
            increment:function(){
                change(1)
            },
            decrement:function(){
                change(-1)
            }
            value:function(){
                return a
            }
        }
       }
        var fun1 = fun()
        var fun2 = fun()
        fun1.value()//0
        fun1.increment()
        fun1.value()//1
        fun2.value()//0
        //所以说会存在两个独立的作用域 也可以称做模块方式

非必要情况下并不需要使用闭包 因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响

什么时候使用呢?

就是当前的需求需要使用到闭包的特性,创建私有变量 或者延长变量生命周期

函数中创建函数的弊端

对于函数中直接创建函数是不建议的,因为每个对象对象创建时方法都会重新被创建

当你在一个函数中定义另一个函数时,每次调用外部函数时都会创建一个新的内部函数实例 。这意味着,如果你多次调用外部函数,就会创建多个相同的内部函数实例,每个实例都占用内存空间。

有一个很好的例子可以说明这一点就是:vue中的data为什么是一个函数而不是一个对象 ,原理是一样的就是因为他想要隔绝变量、独立作用域,所以说如果你不需要这个特性的话就避免这样使用,可以将方法挂载到原型链上面。object.prototype.function

js 复制代码
function outerFunction() {
    function innerFunction() {
        console.log('Inner function called');
    }
    return innerFunction;
}

var fn1 = outerFunction();
var fn2 = outerFunction();

console.log(fn1 === fn2); // false

错误示范

下面再给大家举几个闭包使用错误的例子,看看你有没有中招!!!

过渡使用闭包

在这个例子中,闭包中保持了对外部变量 data 的引用 ,使得外部函数中的 data 无法被释放,可能导致内存泄漏。

js 复制代码
 function outerFunction() {
    var data = 'sensitive data';

    function innerFunction() {
        console.log(data);
    }

    return innerFunction;
}

var closure = outerFunction();

事件监听器未正确移除

在这个例子中,忘记移除事件监听器会导致闭包中保持对 DOM 节点的引用,使得 DOM 节点无法被垃圾回收,从而造成内存泄漏

js 复制代码
function addEventListener() {
    var element = document.getElementById('myButton');
    element.addEventListener('click', function handleClick() {
        console.log('Button clicked');
    });
}

addEventListener();

循环中的闭包问题

在这个例子中,闭包中捕获的是循环结束后的 i 的值,而不是循环中每次迭代时的 i 的值。因此,无论调用哪个函数,都会输出循环结束后的 i 的值

js 复制代码
function createFunctions() {
    var result = [];
    for (var i = 0; i < 5; i++) {
        result.push(function() {
            console.log(i);
        });
    }
    return result;
}

var functions = createFunctions();
functions[0](); // 输出 5 而不是期望的 0

上面这个例子有些朋友可能不太理解,一句话解决你们的疑惑,就是闭包是访问外部函数的变量并不是在自己的函数内创建一个新的变量 ,所以说要说是访问,大家访问到的都是一样的,永远访问到的是i的最终值。如何解决这个问题呢

js 复制代码
function createFunctions() {
    var result = [];
    for (var i = 0; i < 5; i++) {
        (function(index) {
            // 在每次迭代中创建一个闭包作用域,并捕获当前循环变量的值
            result.push(function() {
                console.log(index);
            });
        })(i); // 将当前循环变量传递给立即执行函数
    }
    return result;
}

var functions = createFunctions();
functions[0](); // 输出 0

朋友们可以看懂吗?上面说过闭包的特性就是私有化变量,所以上述例子就是给每个函数增加一个闭包作用域,如果还不明白你可以查看上面的闭包模拟私有方法那一块,一样的原理只是变了一个形式而已。

结语

文章中提到的技术点,如果有不懂得或者想要了解更多的评论区告诉我,我会针对性的出一期更详细的文章。

如果此文章对你有帮助,点个赞支持一下

后续还会不定时出一些文章或针对某个领域的系列文章

有兴趣的可以关注一下!

相关推荐
百万蹄蹄向前冲1 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5812 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路2 小时前
GeoTools 读取影像元数据
前端
ssshooter3 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
Jerry3 小时前
Jetpack Compose 中的状态
前端
dae bal4 小时前
关于RSA和AES加密
前端·vue.js
柳杉4 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化
lynn8570_blog4 小时前
低端设备加载webp ANR
前端·算法
LKAI.5 小时前
传统方式部署(RuoYi-Cloud)微服务
java·linux·前端·后端·微服务·node.js·ruoyi
刺客-Andy5 小时前
React 第七十节 Router中matchRoutes的使用详解及注意事项
前端·javascript·react.js