何时产生闭包
- 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
function fn1() {
var a = 1
function fn2() {
console.log(a) // 此处满足在子函数中引用父函数的变量,将产生闭包
}
fn2()
}
fn1()
Call Stack 相当于执行上下文栈
Scopes 相当于作用域链
当用chrome调试工具查看时,可以看到出现了Closure
即闭包
如果将上面的代码改变一下
function fn1() {
var a = 1
function fn2() {
console.log("hello") // 未引用父函数的变量
}
fn2()
}
fn1()
再用chrome查看
可以看到,Closure消失了
从而得出闭包产生的两个必要条件
- 函数嵌套
- 内部函数引用了外部函数的数据(变量/函数)
闭包到底是什么
通过chrome工具可以查看到,闭包是一个包含被引用变量(函数)的对象,并且闭包存在于嵌套的内部函数中
同样使用第一次用到的代码作为例子
function fn1() {
var a = 1
function fn2() {
console.log(a) // 此处满足在子函数中引用父函数的变量,将产生闭包
}
fn2()
}
fn1()
再次查看调试工具
常见的闭包
- 将函数作为另一个函数的返回值
function fn1() {
var a = 1
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f() // 输出2
f() // 输出3
f() // 输出4
var f1 = fn1()
f1() // 输出2
var f2 = fn1()
f2() // 输出2
var f3 = fn1()
f3() // 输出2
– 闭包实际上相当于嵌套函数存储外部引用变量的对象空间,f()
多次执行,相当于对f
中闭包里存储的a多次递增,因此输出的值递增
– 而f、f1、f2、f3
,都是分别生成的函数,内部的闭包对象各自独立,不会互相影响
- 将函数作为实参传递给另一个函数调用
function showDelay(msg, time) {
setTimeout(function() {
console.log(msg) // 此处使用了外部的msg,所以产生闭包
}, time)
}
showDelay('hello', 2000)
闭包的生命周期
- 产生:在嵌套的内部函数定义执行时就产生了
function fn1() {
// 因为存在函数声明提升,因此fn2的定义在fn1代码正式执行之前就已经完成了,闭包也在这时候产生
var a = 1
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1()
================================================================================================================================================
function fn1() {
var a = 1
var fn2 = function() { // 闭包在此处定义执行时才产生
a++
console.log(a)
}
return fn2
}
var f = fn1()
- 死亡:在嵌套的内部函数成为垃圾对象时
function fn1() {
var a = 1
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f = null // 失去引用成为垃圾对象
闭包的作用
- 使函数的内部变量(函数)在函数执行完后,仍然存活在内存中,延长了局部变量的生命周期
- 将变量(函数)定义在闭包中使用,避免污染全局变量
- 定义JS模块
模块中一般包含大量的变量和函数,如果不使用闭包,将会使这些变量(函数)暴露在全局作用域中,这就很有可能会与用户自己的代码产生冲突
闭包的缺点及解决
-
缺点
– 函数执行完后,函数内的变量没有释放,占用内存时间会变长
– 容易造成内存泄露
— 常见的内存泄露:
a. 意外的全局变量function fn(){ a = 1 // 忘记使用var/let/const声明就赋值,导致将变量添加到window上,意外的产生了全局变量 } fn()
b. 没有及时清理的计时器或回调函数
c. 闭包 -
解决
– 能不用闭包就不用
– 及时释放,将存在闭包的函数置为null
测试题
var name = "The Window"
var obj = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name
}
}
}
console.log(obj.getNameFunc()()) // 输出The Window
function fun(n, o) {
console.log(o)
return {
fun: function(m) {
return fun(m, n)
}
}
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3); // undefined, 0, 0, 0
var b = fun(0).fun(1).fun(2).fun(3); // undefined, 0, 1, 2
var c = fun(0).fun(1); c.fun(2); c.fun(3); // undefined, 0, 1, 1