闭包原理及理解

65 阅读4分钟

一、典型案例/场景

function foo() {
  // 注意,由于闭包,name不会被销毁,同样可以对这个值进行访问
  var name = "foo"
  function bar() {
    console.log("bar", name)
  }

  return bar
}

var fn = foo()
fn()

理解

  • 全局环境内容:foo、fn
  • foo环境内容:name、bar
  • bar环境内容:啥也没有

闭包调用就是全局当中,访问bar,而bar有访问上级的作用域变量,此时name就不会被销毁,形成闭包 这个步骤是在词法解析的时候决定name会不会被销毁的

解释

一个函数和对其周围状态的引用捆绑在一起,这样的组合就是闭包

var age = 12+
function getAge() {
  console.log(age)
}

这就是一个闭包

二、原理

JavaScript中的闭包是一个强大且常用的概念,它涉及作用域、函数和变量的关系。闭包允许函数访问其词法作用域外部的变量,即使在函数外部,这些变量已经不再存在。

闭包的原理: 在JavaScript中,每当创建一个函数时,就会创建一个作用域链。作用域链是一个保存了当前函数作用域以及其所有父级作用域的链表。当函数访问一个变量时,如果在当前作用域中找不到该变量,它会向上遍历作用域链,直到找到或者到达全局作用域。当函数返回时,它仍然保留对其外部作用域的引用,形成了闭包。

闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

三、闭包导致的内存泄漏

所谓内存泄漏,就是内存中有些变量并不能被垃圾回收机制回收掉,导致一直存在于内存中。当这些"不能被回收"的变量逐渐增多时,会导致可用内存越来越少。也就是我们所说的内存泄漏

3.1 内存泄漏的案例

function foo() {
  var name = "foo"
  var age = 18
  function test() {
    console.log(name)
    console.log(age)
  }
  return test
}

var fn = foo()
fn()

// 由于不能被垃圾回收机制回收,因此会导致内存泄漏
fn = null // 此时指向为空,此时AO对象会被垃圾回收机制回收掉------从根对象开始,不可达则销毁

四、应用场景

  1. 保护私有变量:通过闭包可以创建一个私有作用域,将变量封装在该作用域内,从而实现变量的私有性,防止外部访问和修改。这在模块化开发中特别有用,可以避免全局命名冲突。
  2. 延迟执行:使用闭包可以延迟函数的执行。例如,可以在一个定时器或事件处理程序中创建一个闭包,以便在未来的某个时间点触发函数执行。
  3. 保存状态:闭包可以用于保存函数的状态,使函数保持对外部变量的引用。这对于需要记住某些信息的场景非常有用,例如计数器、缓存等。
  4. 实现函数工厂:通过闭包可以创建一个工厂函数,用于动态生成其他函数。工厂函数可以使用闭包来记录配置、设置和其他参数,以生成特定的函数实例。

五、总结

  1. 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。例如上述代码:fn = nulll就可以回收掉该对象

  2. 闭包会在自身函数外部,改变本不属于自身的值。因此我们使用一个函数在修改外部的值的时候,要着重小心,因为很容易导致本身意想不到的结果。也就是我们常说的这样的做法容易出现bug