什么是JavaScript闭包

MDN对闭包的定义是:函数与对其状态即词法环境(lexical environment)的引用共同构成闭包(closure)。也就是说,闭包可以让你从内部函数访问外部函数作用域。在JavaScript,函数在每次创建时生成闭包。
闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。
通俗来讲 一个内部函数访问外部函数局部作用域内的变量 就产生了闭包
作用:延申变量的作用范围 ,使局部变量常驻在内存中不被销毁
缺点:变量不被销毁,发生内存泄漏

我们先通过一个错误的例子了解局部作用域和垃圾回收机制
    function func() {
        var num = 10;
	console.log(num);	
    }
    func();//输出10
    console.log(num);//报错 num is not defined

结论:在一个函数的外部无法访问函数内部作用域定义的变量 即函数的局部作用域。而在函数内部,func函数后 1.定义变量 2.被console.log 输出 3.被调用之后 退出函数 变量i失去引用 转而被当垃圾回收。
在javascript中,如果一个对象不再被引用,那么这个对象就会被垃圾回收机制回收;

再来看一个简单的闭包案例
   // 内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。
    function fn() {
        var str = "这是一段字符!";
        return function () {
            return str;
        }
    }
    let f =fn();
    console.log(f());//输出字符串 被返回的方法外部访问了fn函数局部作用域内的变量str 产生了闭包
    // f = "这是一段字符!";
    // f()函数称为 闭包函数

闭包函数:声明在一个函数中的函数,叫做闭包函数。
结论:被当作返回值的方法f 在fn()方法的局部作用域中访问了str变量产生了闭包,同时我们在fn()函数外部通过变量 f 函数接收了返回值,f = str,成功访问了函数局部作用域内的srt变量; 变相的 srt变量也延申了其值到全局;

两个案例了解闭包的作用

定义一个数组 三秒后打印其内容
var arr = [11,22,33];

//1.常规方法
   for (var i = 0;i<arr.length;i++){
        setTimeout(()=>{
            console.log(arr[i]);//打印输出
        },3000)
   }
//

结果:报错
undefined*3
原因:for循环是同步函数,而timeout的回调函数是一个异步函数,for循环执行完成之后, 其中的变量 i 已经被后续for循环的i++操作重新赋值 现在的值为3,导致报错。
解决方案:通过闭包 对每次for循环产生变量i值进行引用 产生闭包作用域 保留变量存活状态 但是内存中增加了 i=0,i=1,i=2;四个值 产生内存溢出。

//2.采用闭包方式
   for (var i = 0;i<arr.length;i++){
	//通过for循环 产生4个立即函数 并将i值传入 产生引用关系
        (function (i) {
            setTimeout(()=>{
                console.log(arr[i]);//打印输出
            },3000)
        })(i)
	//Timeout中的回调方法访问了立即函数中的变量 i 产生闭包
    }

结果:
[0,1,2,3]
正常输出,延申变量i的使用范围 产生闭包

下面再来看一个经典案例

点击三个按钮 获取其索引值

<ul>
    <li>
        <button>按钮1</button>
    </li>
    <li>
        <button>按钮2</button>
    </li>
    <li>
        <button>按钮3</button>
    </li>
</ul>
<script>

//1.获取元素
var lis = document.querySelectorAll("button");

//2.通过动态添加属性方式 解决for循环是同步方法的问题
    // for(var i = 0;i<lis.length;i++){
    //     lis[i].index = i;//动态添加属性
    //     lis[i].onclick = function () {
    //         console.log(i);
    //         console.log(this.index);//通过调用onclik方法的对象 输出动态添加的属性值
    //     }
    // }


//3.通过闭包实现
for(var i = 0;i<lis.length;i++){
        //利用for循环创立4个立即执行函数
        (function (i) {
	    //向立即执行函数中传递当前i变量的值 产生引用外部遍历关系  产生了闭包,立即执行函数就是闭包函数
            lis[i].onclick = function () {
               console.log(i);
           }
        })(i)
    }

</script>
立即执行函数

js立即执行函数可以让你的函数在创建后立即执行,可以让你的函数在定义后立即被执行,这种模式本质上就是函数表达式(命名的或者匿名的),在创建后立即执行。

常见方式

   //1.加括号
    (function (传值) {
        
    })(传值)


   //2.加特殊符号 +-x~! void  new都可以
    !function (str) {
      console.log(str)
    }("这是字符!")

作用:
1.创建一个独立的作用域。内部变量外部函数无法访问。
2.避免其它全局变量污染,函数为匿名函数。
3.使用作用域外的变量可通过参数传值进行引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值