前言
🙋♂️ 知其然,更知其所以然,举一反三,融会贯通
闭包是js早期谈论的比较多的一个知识点,很多人可能都理解,但是想说清楚却又很难,今天再来温故一下这个知识点,闭包本质是和作用域有着千丝万缕的关联,所以作用域也有必要了解一下。
作用域
概念
定义:作用域是在程序运行时代码中某些特定部分中的变量,函数和对象的可访问性,这是比较专业的定义,换个意思理解,作用域决定了代码区域中变量和其他资源的可见性(是否有权限能被范围到),其实从字面上面理解都比较狭隘,具体可以通过代码层面去深入理解。
分类
目前作用域一共可以分为3类
全局作用域
简单理解是代码中任何地方都能访问到的对象拥有全局作用域, ,例如网页中window对象是就一个全局对象,或者在script脚本中定义的变量,其后续代码都可以访问到这个变量,那么在script脚本内那个变量就具有全局作用域。
一般情况下我们尽量避免去声明全局变量,一方面是容易造成变量污染,另一方面声明太多可能会造成内存泄漏。
<script>
const title = 'hello world'
function getTitle() {
console.log(title) // hello world
}
console.log('title')
getTitle() // hello world
</script>
函数作用域
一般指在函数内定义的变量对象,跟全局作用域相比,影响的范围更小,只能在函数内部被访问到,在函数外部访问不到,函数执行完毕之后,内部的变量对象就会被释放掉
<script>
function getTitle() {
var title = 'hello world'
console.log(title)
}
function getValue() {
console.log(title)
}
getTitle() // hello world
getValue() // ReferenceError: title is not defined
console.log('title') // ReferenceError: title is not defined
</script>
块级作用域
ES6中新增的作用域,用中括号{...}包裹的代码块,并且使用let或const声明的变量对象具有块级作用域,只能在中括号{}内部区域被访问到
<script>
{
var title = 'hello world'
}
console.log(title) // hello world
</script>
如果在中括号中使用var声明变量,不具有块级作用域
<script>
{
let title = 'hello world'
}
console.log(title) // ReferenceError: title is not defined
</script>
作用域链
作用域链与原型链的概念有点类似,js程序执行读取变量时,会先在自身所处的作用域去查找变量,如果找不到,就会去上层的作用域查找,这样一步一步的找到顶层的全局作用域,如果还是访问不到,就会返回undefined
闭包
闭包是一个比较专业的术语(计算机用语),单从字面语义或结合JS而言很难去理解,这对初学js的开发者来说是最迷糊的。
闭包定义:能够读取(有权限访问)其他函数内部变量的函数,这是大多数闭包相关资料中的定义,其实也是很抽象的, 另一种说法是,一个函数嵌套另一个函数,执行函数时返回嵌套的函数,嵌套的函数中有对外部函数变量的引用。
function myFun() {
var total = 1
return function() {
return total++
}
}
const fun = myFun()
console.log(fun()) // 1
console.log(fun()) // 2
从上面例子可以看出,total变量为myFun的局部变量,在函数外部是访问不到的,但是通过返回一个函数的形式,就可以对它进行访问和操作了,那么闭包形成的条件是什么呢?
- 在函数内部返回一个引用类型,可能是对象,数组或函数,大多数情况下是函数
- 返回的引用类型(对象,数组或函数),对外部函数中定义的局部变量进行了引用(使用)
- 在函数外部,有变量来引用函数返回的引用类型,通过变量就可以读取和操作内部的变量了
闭包的作用
- 让函数内的局部变量能长期保存在内存中,不被销毁
- 形成私有的作用域,避免全局变量的污染,闭包中的变量无法在外部为访问到,只能在闭包内部调用访问
闭包的缺点
说的最多的是内存泄漏吧,因为形成闭包后,局部变量不会被垃圾回收,导致一直存在内存中,要注意手动释放内存,我觉得能有多少变量,造成泄漏的可能性不大😹