• 作者:老汪软件技巧
  • 发表时间:2024-10-10 15:05
  • 浏览量:

前言

在腾讯的一面中被面试官问到了闭包问题,没答上来。很是遗憾,感觉自己还是挺了解的,但就是说不清楚。于是在字节的一面里,我又没答上来...

悔恨的泪水不争气的流了下来,于是我决心搞定闭包。终于在第三次面试的时候,把闭包答上来了,亡羊补牢,也不管晚不晚了。

正文

要了解闭包,我们需要先了解词法作用域和作用域链。

词法作用域

在编程语言中,变量和函数的作用域是由其在源代码中的位置决定的。也就是说,在变量和函数被声明的时候其作用域已经确定了。当在函数中访问外部变量时,不论函数运行在哪里,访问的变量始终是其作用域中的那个变量。

let name = 'Agan'
export function getName(){
    console.log(name)
}

例如,我们在这里声明了name 变量和getName 函数,在函数中打印该变量,再将函数导出。如果我们在其他地方引入getName 并运行,则依旧会打印Agan。听起来似乎和我们平时开发时的行为是一致的?没错,在开发中正是一直遵循这套规则。

作用域链

作用域链(Scope Chain)是JavaScript 中用于解析变量的机制。它由多个作用域组成,形成一个链条,确保在查找变量时能够依次访问外部作用域中的变量。

工作原理当前作用域:当代码执行时,JavaScript 首先在当前作用域中查找变量。外部作用域:如果在当前作用域中找不到该变量,JavaScript 会向外层作用域(即父作用域)查找,知道找到变量或到达全局作用域为止。如果在所有作用域中都找不到该变量,JavaScript 会抛出一个错误,表示该变量未定义。

示例:

let globalVar = 'I am global';
function outerFunction() {
    let outerVar = 'I am outer';
    function innerFunction() {
        let innerVar = 'I am inner';
        console.log(innerVar);   // 输出: I am inner
        console.log(outerVar);    // 输出: I am outer
        console.log(globalVar);   // 输出: I am global
    }
    innerFunction();
}
outerFunction();

闭包

内存泄露漏洞_内存泄漏js_

在了解词法作用域和作用域链后,我们可以尝试总结一下闭包的概念。

在嵌套函数中,内部函数可以访问外部函数的变量,就形成了闭包。如果可以通过其他方法调用该内部函数,即使外部函数执行完毕也可以访问到闭包空间中的变量。

举个栗子:

function person() {
    const name = 'Agan'
    function getName() {
        console.log(name)
    }
    return getName
}
let print = person()
print()

我们将person 函数执行,并赋值给print,再执行print 函数,也就是在外部执行getName 函数,它将会打印'Agan'。

这样的话,即使外部函数执行完毕,闭包空间中变量仍然可以被访问到。

内存泄漏

关于闭包的应用场景在这里就不介绍了。我们来看看闭包引起的内存泄漏问题,这也是经常会伴随着闭包的问题。

在上面的代码中,我们随时可以通过在外部调用print 函数访问到name 变量,这就意味着这块内存空间始终被name占据,即使我们不使用,也不会被释放,这就造成了内存泄漏。

如何解决呢?

我们只需要在print 函数使用完毕后,并且确定不再需要使用的时候,将print 赋值为null 就好,即print = null。

原理:

这么做是因为在js 的垃圾回收机制中,当一个变量无法被直接或间接访问到的时候,在下一次进行垃圾回收时,将会回收这个变量。

上面代码中,在person 函数执行完毕后,我们只能通过print 函数间接访问到name 变量,当我们将print 赋值为null 后,name 变量将永远无法被访问到,于是它将会被垃圾回收器处理。

挖坑

下期讲讲垃圾回收机制...