什么是JavaScript闭包

不羁的心 提交于 2020-02-05 09:39:47

JS闭包

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外部访问了fn函数局部作用域内的变量str 产生了闭包
    // f = "这是一段字符!";
    // fn()函数称为 闭包函数

结论:被当作返回值的方法 在fn()方法的局部作用域中访问了str变量产生了闭包,同时我们在fn()函数外部t通过变量 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.使用作用域外的变量可通过参数传值进行引用

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!