深入理解闭包(closure)

253

0.什么是闭包

闭包指的是那些引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的. 只要出现引用了外部变量的函数,那么这个现象叫做闭包现象

1.闭包的特点

弊端:外部函数结束,内部函数依然存在,而且占用了外部函数的变量,导致外部函数不能释放内存,产生内存泄漏. 优势:1.变量是在函数内部定义(外部函数)属于局部变量,不会被外部污染 2.内部函数可以访问定义它们的外部函数的参数和变量(除了thisarguments)

2.解决闭包的麻烦

当内部函数不再使用的时候,主动释放闭包所占用的内存.(看demo)

3.demo

这里的内部函数是一个匿名函数,嵌套在外部函数setElement中,其中引用了外部函数的变量indexarr,在这个内部函数被返回并在其他地方被使用时,它依然用着这两个变量,所及即使外部函数完成调用后,依旧没有释放内存. 这里创建的函数会被保存在变量getElement中,把getElement设置为null会接触对函数的引用,从而让垃圾回收系统释放,同时作用域也会被销毁.

<body>
    <button>自增</button>
    <h1></h1>
    <script>
        const btn = document.querySelector('button');
        const h1 = document.querySelector('h1');

        // 闭包的特点:外部函数结束,内部函数依然存在,而且占用了外部函数的变量
        //(导致外部函数不能释放内存)

        function setElement() {
            // 1. 定义一个数组
            let arr = [
                { name: "金" },
                { name: "木" },
                { name: "水" },
                { name: "火" },
                { name: "土" }
            ];

            // 2. 定义一个下标
            let index = -1;         // 不是数组真正有效的下标

            // 3. 返回一个函数
            return function () {
                // 3.1  修改index
                index++;            // 当前函数没有index,访问的是外部的index

                // 3.2 判定
                if (index > arr.length - 1) {
                    index = 0;     //这时显示下标0,"金"
                }

                // 3.3 返回当前的数据
                return arr[index].name;
            };
        }

        // 调用函数setElement
        let getElement = setElement();

        // 代码执行到这:函数setElement()执行已经结束
        // 按理说:应该释放setElement的内存(index,arr)
        // 但是:因为当前函数的运行返回了一个函数:里面要用到index和arr:导致函数不能
        //释放内存(内存一直被占用): 闭包
        // console.log(getElement);

        h1.innerText = getElement();//渲染

        // 点击事件
        // 点击调用函数,下标++显示不同数据
        btn.onclick = function () {
            h1.innerText = getElement();
        };


        // 假设后续还有很多代码要运行:上述内存一直会占用(虽然代码很安全,不存在污染的问题)
        // 解决闭包的麻烦:主动释放闭包所占用的内存


        // 释放内存把下面这行代码注释打开
        // getElement = null;          
        // getElement不再指向闭包函数function,闭包函数也就不需要占用变量arr和index:setElement函数可以得到释放

    </script>
</body>

4.this对象

在闭包中使用this会比较复杂

  • 如果内部函数没有使用箭头函数定义,则this会在运行时绑定执行函数的上下文.
  • 如果在全局函数中调用,则this在非严格模式下等于window,严格模式下尾undefined
  • 如果作为某个对象方法调用,则this指向该对象
window.identity = '这是window'
        let obj = {
            identity: '这是obj',
            getIdentity() {
                // 返回内部函数
                return function () {
                    return this.identity
                }
            }
        }
        console.log(obj.getIdentity()());//这是window
        // 注意最后还有()

这里的this并没有指向当前作用域,是因为thisarguments都是不能直接在内部函数中访问,如果想要访问arguments可以用内包内可以访问的变量保存.例如:let that = this

 <script>
        window.identity = '这是window'
        let obj = {
            identity: '这是obj',
            getIdentity() {
                let that = this
                // 返回内部函数
                return function () {
                    return that.identity
                }
            }
        }
        console.log(obj.getIdentity()());//这是obj
        // 注意最后还有()
    </script>