闭包在js中应用非常广泛,也是极为重要的知识点。刚开始了解闭包时也是云里雾里,花了好长时间才搞明白。下面来详细叙说一下闭包的知识。
要理解好闭包的概念,我们首先得清楚下面的三个知识点:
1、作用域
变量作用域分为全局变量
和局部变量
先看下面一段js代码
var name = '小明'
function man(){
console.log(name)
var age = '20'
}
man()
console.log(age)
输出的结果:
man()
输出小明
,console.log(age)
报错age is not defined
上述代码中name
在全局作用域
下,即为全局变量
。age在函数作用域
下,即局部变量
。而JavaScript的运行机制为函数内部可以访问外部的全局变量
,反之,函数外部无法读取函数内部的局部变量
。
2、链式作用域结构
那如何得到上图中age
的值呢,尝试在man()
函数里面添加一个函数,如下:
var name = '小明'
function man(){
console.log(name)
var age = '20';
(function getage(){
console.log(age)
}())
}
man()
结果:第6行输出:20
getage
函数存在于man
函数的内部,age
变量为局部变量。前面作用域的例子说明了man
函数外部不可以访问age
,但是man
函数变量对getage
函数是可见的。当前反之,getage
里的变量man
无法访问。这就是js的链式作用域结构
。
概括一下上面的话:首先子对象会在自身的作用域寻找变量,如果没有则一层一层的向父级寻找。所以,父级作用域的变量对子作用域可见,反之不可见
3、垃圾回收机制
垃圾回收机制的知识点很多,下面简单的谈一谈。
变量生命周期
一个变量不再被使用,即生命周期结束,它所指向的内存会被垃圾回收机制收回。全局变量在浏览器关闭时结束生命,局部变量在函数执行完毕时结束。
回收机制
(1)如果一个对象不再被引用,那么它指向的内存会被收回。
(2)如果两个对象互相引用,不在被第三个引用也会被回收
(3)如果两个对象互相引用,其中一个别第三方引用,那么就不会回收这些变量。
前面说链式作用域的时候,子函数
getage
可以访问到父函数man
里的局部变量。
那么如何让man函数外部访问man函数的局部变量呢?
只要将子函数getage作为父函数man的返回值,那么就可以在man的外部访问到了。
代码如下:
function man(){
var age = 20
function getage(){
console.log(age)
}
return getage
}
var showage = man()
showage()
结果:showage()
执行之后输出age
。
那么这段代码有什么特点呢?
- 函数a包含函数b
- 函数a返回函数b
- 函数b在函数a外部被引用
对于上面的代码,getage即为一个闭包
。
闭包概念: 闭包是一个可以读取函数内部局部变量的函数
也就是说,当函数a内部创建的函数b被外部引用时,就创建了闭包b
1、访问局部变量
因为函数内部的变量为局部变量,外部无法使用。当需要将变量提供给外部时使用闭包方式。
function man(){
var age = 20
function getage(){
console.log(age)
}
return getage
}
var showage = man()
showage()//20
2、保存局部变量
前面说到,因为垃圾回收机制的存在,函数执行完成后里面的局部变量会被销毁,但有时我们希望可以被外面访问到并且保存每一次的执行结果
例子:
function num(){
var num = 0
return function addnum(){
console.log(++num)
}
}
var changnum = num()
changnum()//1
changnum()//2
changnum()//3
这里局部变量num
始终保存在内存
中,每一次调用都在上一次的基础上加1。
3、实现类的封装
属性为私有属性外部不可访问,通过闭包将访问属性、修改属性的方法提供给外部使用。
function Man(){
var name = '小明'
var age = 20
return {
showname:function (){
return name
},
changename:function (newname){
name = newname
}
}
}
var m = new Man()
console.log(m.showname())//小明
m.changename('小黑')
console.log(m.showname())//小黑
1、内存泄漏
示例:
function num(){
var num = 0
return function addnum(){
console.log(++num)
}
}
var changnum = num()
changnum()//1
changnum()//2
changnum()//3
changnum = null//解除num()的引用,此时变量num释放
2、谨慎修改变量
因为通过闭包你可以对里面的局部变量进行修改,如果通过闭包去实现一个类,那么可以通过类公有的方法去操作私有属性,这里需要注意使用。
闭包在JavaScript使用非常广泛,也是重要的内容,高级应用的必备。