前言
对于每个正在学习JavaScript的童鞋们,相信你们或多或少的都在百度上搜索过闭包,但百度过后对闭包的理解就更加模糊了。作为学习JavaScript中的一员,我也不例外。接下来我尽量用一种很容易让我们接受的一种方式来讲述闭包。
概念
当你在编写JavaScript代码时,如果当你在一个函数中嵌套了一个或者多个函数,并且将其中的几个函数作为返回值返回到了全局,这样返回的几个函数和最外层的函数就构成了闭包。
function a (){
var a = 3
return function b (){
console.log("闭包的构成")
}
}
let c = a()
c()
从作用域链来理解闭包
在函数执行前的非常小的一段时间内,会有一个全局GO对象,和与函数相关的AO对象生成。这些对象会去收集声明的变量和函数。具体的过程如下:
创建AO对象(Activation Object)
- 创建对象AO
- 找到函数的形参和变量声明,将变量声明和形参作为AO的属性名,值为undefined
- 将实参和形参值统一
- 在函数体里找函数声明,将函数名作为AO对象的属性名,值赋予函数体
创建对象GO(Global Object)
- 创建对象GO
- 找到变量声明,将变量声明作为GO的属性名,值为undefined
- 在全局里面找函数声明,将函数名作为GO对象的属性名,值赋予函数体
// AO: {
// a: undefined 1 3
// b: undefined function(){} 2
// c: undefined 0
// d: function(){}
// }
function test(a, b) {
console.log(a)
c = 0
var c
a = 3
b = 2
console.log(b)
function b() {}
function d() {}
console.log(b)
}
test(1)
说明
被执行的返回函数们可以共享外层函数的AO对象里面的一些属性,可以以下一节第二个例子为参考。
闭包的作用
- 实现公有变量
function test() {
var count = 0
return function a (){
count++
console.log(count)
}
}
let fn = test()
fn() //1
fn() //2
fn() //3
- 做缓存
function fruit () {
var food = 'apple'
var obj = {
eatFood: function () {
if(food != ''){
console.log(`I am eating ${food}`)
food = ''
}
else{
console.log('There is nothing!')
}
},
pushFood: function (myfood) {
food = myfood
}
}
return obj
}
var person = fruit()
person.eatFood()
person.eatFood()
person.pushFood('banana')
person.eatFood()
闭包对于更高级的开发还有一下特点
-
可以实现封装,属性的私有化
-
模块化开发,防止污染全局变量