闭包

2024-01-17 19:43:22

全局变量和局部变量

1. 全局变量

在任何地方都可以访问到的变量就是全局变量。

无论let、const还是var,在全局作用域下的都是全局变量。

不管在函数作用域还是块级作用域下,如果省略var、const、let定义的变量都是全局变量。

2. 局部变量

块级作用域下({},需要注意,在块级作用域下,通过var定义的变量是全局变量,let、const定义的变量是局部变量 )、函数等定义的变量,是局部变量。

什么是闭包

官方说法:闭包就是指有权访问另一个函数作用域中的变量的函数。 MDN说法:闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。

当执行函数A时,会创建一个函数执行上下文,并将函数执行上下文进入栈中,并创建函数中的作用域链、this、arguments等。在函数内部代码执行过程中,存在一个另一个函数B,并且这个函数B中存在对当前函数A中部分变量的引用。当函数A执行结束时,被函数B引用的变量并不会被垃圾回收(除非当函数B被垃圾回收),此时就形成了一个闭包。

function abc() {
    const name = "GYQ";
    function getName() {
        console.log(name);
    }
    getName();
}

闭包

注意:MDN的定义不是很准确,ES6之后,产生了一个块级作用域。如果在块级作用中定义的let、const变量(被称为局部变量),如果在定义一个函数,并在函数中引用块级作用域中的变量,此时并不会产生一个闭包。

if (true) {
    let abc = 123;
    const ab = function() {
        console.log(abc);
        debugger;
    }
    ab();
}

闭包

闭包作用

  1. 使用闭包,我们可以访问到函数内部的变量,从而创建函数的私有变量。
function transition() {
    const listeners = [];
    return {
        add(listener) {
            listeners.push(listener);
            return function() {
                const index = listeners.indexOf(listener);
                if (index > -1) {
                    listeners.splice(index, 1);
                }
            }
        }
    }
}
  1. 保存已经运行结束的函数内部的部分局部变量,使其不被释放(垃圾回收)。

使用场景

  1. IIFE(立即执行函数)
  2. 节流防抖
  3. 函数柯里化
  4. return 回一个函数

闭包经典面试题

const arr = [];
for(var i=0; i<5; i++) {
    arr[i] = setTimeout(() => {
        console.log(i); // 输出 5 5 5 5 5
    }, 0);
}

修改后

const arr = [];
for(var i=0; i<5; i++) {
    (function(i) {
        arr[i] = setTimeout(() => {
            console.log(i); // 输出 0 1 2 3 4
        }, 0);
    })(i);
}

闭包不会造成内存泄露

闭包造成内存泄露,是IE8及之前版本的bug。

在IE8以及更早版本的IE中,BOM和DOM对象并非是原生JavaScript对象,它是由C++实现的 组件对象模型对象(COM,Component Object Model),而COM对象使用引用计数算法来实现垃圾回收,所以即使浏览器使用的是标记清除算法,只要涉及到COM对象的循环引用,就还是无法被回收掉,就比如两个互相引用的DOM对象等等,而想要解决循环引用,需要将引用地址置为null 来切断变量与之前引用值的关系,如下:

function abc() {
    let ele = document.getElementById("root");
    let obj = {};
    // 造成循环引用
    obj.ele = ele;
    ele.obj = obj;
    // 切断引用关系
    obj.ele = null;
    ele.obj = null;
    ele = null;
    obj = null;
}

不过在IE9及以后的BOM与DOM对象都改成了JavaScript对象,也就避免了上面的问题。

内存泄漏是什么

是指由于疏忽或错误造成程序未能释放已经不再使用的内存

哪些情况会导致内存泄漏

以下四种情况会造成内存的泄漏:

  1. 隐式声明的全局变量

    由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。

  2. setTimeout或者setInterval未能及时销毁

  3. DOM对象的引用

    获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收。

  4. 闭包的不合理使用

    不合理的使用闭包,从而导致某些变量一直被留在内存当中。

目录

相关推荐
display为inline-block的元素中间有间隙的原因和解决办法面试题:new关键字做了什么面试题:this指向深入理解JS预解析(变量提升和函数提升)引用计数算法