Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

读书笔记(二): 闭包 #6

Closed
campcc opened this issue Aug 3, 2019 · 0 comments
Closed

读书笔记(二): 闭包 #6

campcc opened this issue Aug 3, 2019 · 0 comments
Labels
你不知道的JavaScript 你不知道的JavaScript 闭包 闭包

Comments

@campcc
Copy link
Owner

campcc commented Aug 3, 2019

JavaScript 语言中一个非常重要但又难以掌握,近乎神话的概念: 闭包

垃圾回收机制

解释什么是闭包之前,有必要简单了解下垃圾回收的机制。

JavaScript中,如果一个对象不再被引用,那么这个对象就会被垃圾回收机制回收,如果两个对象相互引用,而没有被第三者引用,那么这两个相互引用的对象也会被垃圾回收机制回收。

垃圾回收的实现方式有 引用计数标记清除,引用计数无法解决循环引用的问题,现代浏览器的垃圾回收机制实现要复杂的多,本文的主角不是垃圾回收机制,贴一个 V8传送门,有兴趣的读者可以深入了解。

什么是闭包

闭包是函数和声明该函数的词法环境的组合。简单来说,当函数可以记住并访问其所在的词法作用域时(即使函数是在当前词法作用域外执行),就产生了闭包。其实通过枯燥的定义和术语来理解闭包是不推荐的,最好的方式还是结合代码:

function foo() {
  var name = 'foo' // 外部函数定义的变量

  return function bar() { // 外部函数返回了一个内部函数
    console.log(name) // 内部函数内保留了对外部函数定义的变量的引用,这里是一个 RHS 引用查询
  }
}

var baz = foo() // 一个引用函数的变量可以看做有两个指针,一个指向函数的指针,一个指向闭包的隐藏指针。正常来说,foo 函数执行完后就会被垃圾回收机制回收,其作用域链也会被销毁,但是因为闭包的原因,其内部函数保留了其所在的词法作用域内一个变量 name 的引用,所以执行 baz 时仍然可以访问这个变量
baz() // foo

闭包是函数吗

确切来说,不是。虽然在《JavaScript高级程序设计》中作者描述,闭包是指有权访问另一个函数作用域中的变量的函数《JavaScript权威指南》中也有关于闭包的解释,从技术的角度讲,所有的JavaScript函数都是闭包:它们都是对象,它们都关联到作用域链。不可否认,在 JavaScript 中,如果你在另一个函数中使用了 function 关键字(eval也会创建闭包),就创建了一个闭包,但是闭包不只是函数,还应该包括函数可访问的词法作用域。

闭包存储在哪里

在早期的 MDN 版本中,认为闭包是引用了自由变量的函数,让很多人误认为自由变量是存储在堆内存中,现在看来这种说法并不准确。

JavaScript 中数据到底是存储在 还是 中,跟数据的类型有关。

中一般存放一些简单数据段,对应到 JavaScript 的数据类型,就是基本数据类型:String, Boolean, Number, Symbol, Undefined, Null 中存储的是基本数据类型的值,访问的方式是按值访问。

引用类型的值存放在 中,也就是 堆内存 中的对象,如果一个变量的类型是引用类型,那么变量中保存的实质上是一个指针,也就是一个内存地址,这个内存地址存放在 中,访问的方式是,首先从 中读取内存地址,然后沿着指针找到 中对应的对象。

我们知道,闭包包含了函数及其可访问的词法环境,这里的词法环境实质上是一条作用域链,作用域链上可能有基本类型的变量,也有引用类型的变量,所以确切的说,闭包存储在堆栈中而非堆中。

闭包的缺陷

闭包的缺陷主要有两种:占用内存可能造成内存泄漏

占用内存很好理解,因为闭包创建时携带了可访问的词法环境,为什么说闭包可能造成内存呢?

这个主要是针对老版本的 IE 而言。我们知道,内存泄漏最常见的原因就是因为循环引用,在 IE 早期的垃圾回收处理机制中,使用的是引用计数的方式,在 IE8 及其以下的浏览器中的 DOM 和 BOM 对象中创建闭包(比如绑定事件回调),如果不清除循环引用手动释放,就会造成内存泄漏。

闭包的应用

  • 封装私有变量和方法
  • 实现独立作用域
  • 实现单例模式
  • 回调和计时器
  • 绑定上下文(如 bind 的实现)
  • 偏函数
  • 模块的实现

(完)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
你不知道的JavaScript 你不知道的JavaScript 闭包 闭包
Projects
None yet
Development

No branches or pull requests

1 participant