目录
1.引入:
我们现在有个需求:有三个按钮,当点击某个按钮时,提示“点击的是第n个按钮”。
代码实现:
var btns = document.querySelectorAll('button')
//遍历加监听
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function () {
console.log(`第${i + 1}个按钮`)
}
}
这样写的方式,不管点击哪一个按钮,都会出现“第4个按钮”。
这是因为点击按钮事件只是触发的时候才会调用。当执行到console.log(`第${i + 1}个按钮`)这句代码,点击事件执行时,for循环已经执行结束。整个过程最终只产生了一个i,此时i的值已经变成了3,不管点击那个按钮都是i=3,所以输出3+1=4,就是“第4个按钮”。也就是说,前3次循环时,点击事件并没有执行。
我们可以对代码进行改造:
我们对i进行记录,把每个btn的i相对应起来,知道每个btn按钮的i索引值。这样点击按钮,就会输出相对应的数值。
var btns = document.querySelectorAll('button')
for (var i = 0; i < btns.length; i++) {
//将btn所对应的索引值保存到btn上
btns[i].index = i
btns[i].onclick = function () {
console.log(`第${this.index + 1}个按钮`)
}
}
此时可以正常输出。
当然,还有一种方式,将输出语句放在一个匿名的自调用函数里面。
var btns = document.querySelectorAll('button')
for (var i = 0; i < btns.length; i++) {
(function (i) {
btns[i].onclick = function () {
console.log(`第${i + 1}个按钮`)
}
})(i)
}
此时也可以正常输出。
这个就用到了闭包。
2.闭包是什么呢?
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包。
function fn1() {
var a = 2
var b = 'abc'
function fn2() {
console.log(a)
}
fn2()
}
fn1()
就是内部嵌套的fn2函数引用了外部fn1函数里的变量a,此时fn2就产生了闭包。
那么闭包到底是什么?
①理解一:闭包是嵌套的内部函数,在这里就是fn2这个函数。
②理解二:包含被引用变量(函数)的对象,也就是在fn2这个内部函数里所引用的变量a。
注意:闭包存在于嵌套的内部函数中。
3.闭包产生的条件:
①函数嵌套
②内部函数引用了外部函数的数据(变量/函数)
③执行调用外部函数
4.常见的闭包
①将函数作为另一个函数的返回值
function fn1() {
var a = 2
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f()//3
f()//4
内部函数fn2引用了外部函数fn1的变量a,产生了闭包。
给var a = 2和a++打上断点整个程序运行过程如下:
1.fn1和fn2作为函数提升,fn2中的a++此时为2。
2.语句到return fn2。
3.执行第一个 f()。
4.进入到fn2中执行a++,此时为3,闭包仍存在。
5.语句执行之后再到第二个f()。
6.接下来直接进入到fn2()中执行a++,此时为4。
7.结束。整个过程产生一个闭包,因为fn1只调用了一次。
如果没有闭包,fn1函数一执行,变量a就消失了。后面再调用f()时,就会报错。
②将函数作为实参传递给另一个函数调用
function showDelay(msg, time) {
setTimeout(function () {
alert(msg)
}, time)
}
showDelay('message', 2000)
内部函数引用了外部函数的message,产生了闭包。
5.闭包的作用
①使用函数内部的变量,也就是局部变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期);
②让函数外部可以操作(读写)到函数内部的数据(变量/函数)。
6.闭包的生命周期
①产生:在嵌套内部函数定义执行完时就产生了(函数对象创建时,不是在调用)
②销毁:在嵌套的内部函数成为垃圾对象时
function fn1() {
var a = 2//此时闭包就已经产生了(函数提升,内部函数对象已经创建了)
function fn2() {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f()//3
f()//4
f=null//闭包销毁(包含闭包的函数对象成为了垃圾对象)
7.闭包的应用
定义JS模块:
*具有特定功能的js文件
*将所有的数据和功能都封装在一个函数内部(私有的)
*只向外暴露一个包含n个方法的对象或函数
*模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
code.html文件:
<script src="./code.js"></script>
<script>
var module = myModule()
module.doSomething()//doSomething()MY CODE
module.doOtherthing()//doOtherthing()my code
</script>
code.js文件:
function myModule() {
// 私有数据
var msg = 'My code'
// 操作数据的函数
function doSomething() {
console.log('doSomething()' + msg.toUpperCase())
}
function doOtherthing() {
console.log('doOtherthing()' + msg.toLowerCase())
}
// 向外暴露对象(给外部使用的方法)
return {
doSomething: doSomething,
doOtherthing: doOtherthing
}
}
另一种方式实现:
code.html文件:
<script src="./code2.js"></script>
<script>
myModule.doSomething()//doSomething()MY CODE
myModule.doOtherthing()//doOtherthing()my code
</script>
code.js文件:
(function myModule() {
// 私有数据
var msg = 'My code'
// 操作数据的函数
function doSomething() {
console.log('doSomething()' + msg.toUpperCase())
}
function doOtherthing() {
console.log('doOtherthing()' + msg.toLowerCase())
}
// 向外暴露对象(给外部使用的方法)
window.myModule = {
doSomething: doSomething,
doOtherthing: doOtherthing
}
})()
8.闭包的缺点及解决
缺点:
①函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
②容易造成内存泄露
解决:
①能不用闭包就不用
②及时释放
function fn1(){
var arr=new Array[1000000]
function fn2(){
console.log(arr.length)
}
return fn2
}
var f=fn1()
f()
f=null//让内部函数成为垃圾对象,进而回收闭包对象arr
补充:内存溢出与内存泄漏
1.内存溢出:一种程序运行出现的错误。当程序运行需要的内存超过了剩余的内存时,就会抛出内存溢出的错误。
代码示例:(不要轻易尝试,会死机,哈哈哈)
var obj = {}
for (var i = 0; i < 10000; i++) {
obj[i] = new Array(10000000)
console.log('----')
}
Out of Memory(内存不足)
2.内存泄漏:占用的内存没有及时释放,可用内存变小,此时程序还能正常运行。
内存泄漏积累多了就容易导致内存溢出。
常见的内存泄漏:
①意外的全局变量
function fn() {
a = 3
console.log(a)
}
②没有及时清理的计时器或回调函数
setInterval(function(){
console.log('11')
},1000)
解决:
var timer=setInterval(function(){
console.log('11')
},1000)
clearInterval(timer)//清除定时器
③闭包
function fn1() {
var a = 4
function fn2() {
console.log(++a)
}
return fn2
}
var f = fn1()
解决:
function fn1() {
var a = 4
function fn2() {
console.log(++a)
}
return fn2
}
var f = fn1()
f = null