前言
MDN对闭包的解释是这样的:一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。MDN文档
解释有点抽象,不好理解,现在我们用代码的形式来解释下。
一. 闭包概念解析
闭包让你可以在一个内层函数中访问到其外层函数的作用域
(1) 父对象的所有变量,对子对象都是可见的,子对象对父对象是不可见的
// 普通函数
var a =1
function f1() {
a =2 ;
var b = 3;
}
console.log(b)
在f1函数里面是可以访问到函数外部变量a,但是在函数f1外部是访问不到函数内部的变量a的
(2)判断一个函数是不是闭包
在 JavaScript 中,闭包是指函数可以访问外部函数中的变量,且它的变量作用域链不会被销毁。
1.可以在函数外部读取函数内部的变量;
2.可以让变量的值始终保持在内存中;
如果一个函数满足这两个条件,那么它就是闭包函数。
在上述代码中内部函数f2是不能访问到函数外部变量b,所以f2函数不是闭包
(3) 实现一个简单的闭包
function f1() {
function f2() {
}
}
上述代码 f2就是闭包,所以也验证了闭包让你可以在一个内层函数中访问到其外层函数的作用域这个概念
二. 为什么要用闭包?
闭包的作用主要有两点:
(1) 高继承和封装性;
(2) 避免全局变量污染。
示例代码如下
function computer() {
var baseNum = 10;
return {
plus: function(a,b) {
return a + b + baseNum
},
div: function(a,b) {
return a/b/baseNum
}
}
}
let res = computer();
res.plus(10,20)
高集成和封装性是因为可以将多个功能模块封装在一个函数里面,这样便于管理和维护
避免全局污染是因为假如函数内部改变了外部变量的值,而函数外部之后又用到了那个变量,这样在函数外部的使用可能不符合预期,所以闭包就是把该变量包裹在了一个局部的作用域里面,从而达到全局变量污染的目的
三. 闭包是怎么工作的?
上述的res变量为什么可以一直访问并改变computer函数里面的值?
(1)普通函数调用
function f1() {
var baseNum = 10;
function f2() {
baseNum++
console.log(baseNum)
}
f2()
}
f1(); //11
f1(); // 11
根据js垃圾回收机制,f1每执行一次,里面的变量就会释放了,即f1函数里面的作用域链AO部分就断了
(2)闭包函数为啥可以保留之前的调用值
function f1() {
var baseNum = 10;
return function f2() {
baseNum++
console.log(baseNum)
}
}
var res = f1();
res(); // 11
res() // 12
原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这个全局变量始终在内存中,这导致f2始终在内存中,而f2的存在依赖于f1,f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
四. 使用闭包的注意点
(1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
五、闭包实际应用场景
闭包的主要作用就是可以让函数访问外部函数作用域中的变量。这种机制可以使函数拥有独立的作用域,从而避免变量名冲突的问题,并且可以实现私有成员的效果,保护内部变量不被外部访问。
实际上,闭包在日常编程中有很多应用场景
比如:
-
封装变量和方法:将变量和方法封装在匿名函数内部,返回对外部公开的方法,达到了封装的效果;
-
实现模块化开发:可以将代码模块化,避免全局变量污染,确保代码的隔离性;
-
实现柯里化函数:将一个多参数的函数转化为一个单参数的函数,以便延迟执行或者实现函数复用;
-
解决异步操作问题:在异步编程中,可以通过闭包来处理回调函数的作用域问题,避免回调地狱。