1.什么是闭包
- MDN给出的定义是:
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。
简单来说就是,定义在一个函数内部的函数。其中一个内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
- 举一个简单的🌰:
var scope = "global scope" // 全局变量
function init() {
var scope = "local scope" // 局部变量
function internalFun() { return scope}
return internalFun()
}
init() // 结果什么呢?
init()
创建了一个局部变量,并且定义了一个函数internalFun(), 函数internalFun()返回了这个变量的值,最后将函数internalFun()的执行结果返回。注意,internalFun()没有自己的局部变量,然后,因为它可以访问到外部函数的变量,所以在 internalFun()可以使用父函数inint() 中声明的scope变量。
所以输出的结果为为:“local scope”
现在我们对这段代码做一点改动,猜猜会返回什么?
var scope = "global scope"
function init() {
var scope = "local scope"
function internalFun() { return scope }
return internalFun()
}
init()()
在这段代码中,在init函数后面加了一对括号,init函数现在仅仅返回函数内嵌套的一个函数对象,而不是直接返回结果。
回想一下作用域的基本规则:js函数的执行用到的作用域链是函数定义时候创建的,嵌套的函数internalFun()定义在这个作用于链里,其中的变量scope一定是局部变量,不管在何时何地执行函数internalFun(),这种绑定在执行internalFun()时依然有效。因此最后一行代码输出结果为local scope
。
2.闭包的特点是什么
- 函数嵌套函数。
- 函数内部可以引用外部的参数和变量。
- 参数和变量不会被垃圾回收机制回收。
2.1 js的垃圾回收机制
- 如果一个对象不再被引用,那么这个对象那个就会被回收。
- 如果两个对象相互引用,而不在被第三者所引用,那么这两个对象都会被回收。
2.2闭包的用途
- 访问函数内部的变量
- 让变量始终保持在内存中
3.闭包的应用场景
1.使用setTimeout
使用闭包可以保存循环变量或定时器的参数,并确保在回调函数执行时以正确的值进行处理。例如:
for (var i = 0; i < 5; i++) {
(function(index) {
setTimeout(function() {
console.log(index)
}, 1000)
})(i)
}
我们使用立即执行函数创建了一个新的函数作用域,每次循环都将 i
的值传递给立即执行函数的参数 index
,从而在定时器回调函数执行时正确地打印每次循环的值。
2.实现私有方法
JavaScript 没有这种原生支持,但我们可以使用闭包来模拟私有方法。私有方法不仅仅有利于限制对代码的访问:还提供了管理全局命名空间的强大能力,避免非核心的方法弄乱了代码的公共接口部分。例如:
function createPerson(name) {
const greeting = 'Hello, ' + name;
return {
sayHello: function() {
console.log(greeting)
}
}
}
const person = createPerson('lily');
person.sayHello() // 输出: Hello, lily
person.greeting // undefined
3.递归
闭包在递归算法中经常被使用,可以保存递归中的状态和结果,并确保在每次递归调用时使用正确的值。例如
function factorial(n) {
if (n === 1) {
return 1
} else {
return n * factorial(n - 1)
}
}
console.log(factorial(5)) // 输出: 120
4.闭包的优缺点
- 缺点
- 可以减少全局变量的定义,避免全局变量的污染
- 能够读取函数内部的变量
- 在内存中维护一个变量,可以用作缓存
- 优点
- 会造成内存泄漏,闭包的使用会使函数中的变量一直保存在内存中,内存消耗很大,所以不能随意使用,否则会造成网页的性能问题。解决方法就是:
在使用完以后,手动将它赋值为null
- 闭包可能在父函数外部, 改变父函数内部变量的值。
- 由于闭包涉及跨作用域的访问,所以会导致性能损失。