JS闭包及闭包的作用和隐患

何时产生闭包

  • 当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tanleiDD

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值