闭包
循环遍历加监听
var btns = document.getElmentsByTagName("button")
for (var i =0,length=btns.length;i<length;i++){
var btn = btns[i]
//存储btn的所有对应下标,保证对应
btn.index = i
btn.onclick = function () {
alert("第"+(this.index+1)+"个")
}
}
//加索引写法
var btns = document.getElmentsByTagName("button")
for (var i =0,length=btns.length;i<length;i++){//i为全局变量
(function (i){//i为局部变量
var btn = btns[i]
btn.onclick = function () {
alert("第"+(this.index+1)+"个")
}
})(i)
}
//闭包写法
var btns = document.getElmentsByTagName("button")
for (var i =0,length=btns.length;i<length;i++){//i为全局变量
(function (i){//i为局部变量
var btn = btns[i]
btn.onclick = function () {
alert("第"+(i+1)+"个")
}
})(i)//外部函数没调用一次,产生一个闭包,每个闭包存储的i值不同,因而可以对应
}
理解闭包
- 如何产生闭包?
- 调用外部函数,创建声明内部函数,且一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了一个闭包(调用一次外部函数,就产生一个闭包)
- 闭包是什么?
- 使用chrome调试查看
- 理解一:闭包时嵌套的内部函数
- 理解二:包含被引用变量(函数)的对象
- 注意:闭包存在于嵌套的内部函数中
- 产生闭包的条件?
- 函数嵌套
- 内部函数引用了外部函数的数据(变量/函数)
- 调用外部函数,执行内部函数定义,不用调用内部函数
常见的闭包
- 将函数作为另一个函数的返回值
function fn1 () {
var a =2
function fn2() {
a++
console.log(a) //如果没有闭包则fn1()执行完,a就被释放,fn2()报错找不到a变量
}
return fn2//暴露a值
}
var f = fn1()
f()//3
f()//4
- 将函数作为实参传递给另一个函数调用
function showDelay (msg,time) {
setTimeOut(function () {
alert(msg) //产生闭包,注意不包含time,因为内部函数只引用了msg
},time)
}
showDelay("a",2000)
闭包的作用
- 使用函数内部的局部变量在函数执行后,仍然存活在内存中(延长了局部变量的生命周期)
- 让函数外部可以操作(读写)到函数内部的数据(变量/函数)(没有闭包不行)
问题:
- 函数执行完后,函数内部声明的局部变量是否存在?
- 存在于闭包中的变量才可能存在,因为有可能该内部函数对象没被引用当外部函数执行完毕后,会被释放掉
- 在函数外部能直接访问函数内部的局部变量嘛?
- 不能,但是有闭包可以操作(外部函数嵌套内部函数,return内部函数)
【注】在函数内部若没有引用一个变量/函数,会被自动释放
function fn1() {
//此时闭包就已经产生,即函数提升
var a = 2
function fn2() {
a++
console.log(a)
} //会被释放
function fn3() {
a--
console.log(a)
}//fn3不是闭包内的变量,只有a
return fn3 //返回fn3地址值到f
}
var f = fn1() //保存的fn3的地址值,所以没有被释放的原因在这 ,而且fn1执行完,会释放局部变量fn3也是个局部变量所以会被释放,但fn3的函数对象没被释放,f一直引用着!
f() //1
f() //0
f = null // 闭包死亡(包含闭包的函数对象成为垃圾对象)
闭包的生命周期
- 产生:在嵌套内部函数定义执行完成(只是创建了函数对象)时就产生(非调用)
- 死亡:在嵌套的内部函数称为垃圾对象时,即没被引用 f = null
闭包的应用_自定义JS模块
-
具有特定功能的js文件(封装)
-
将所有的数据和功能都封装在一个函数内部(私有的)
-
只向外暴露一个包含n个方法的对象或函数(return{a:函数名,b: 函数名})
(function () { var msg = "a" function doSomeThing() { console.log("b"+msg) } function doOtherThing () { console.log("c"+msg) } //向外暴露 window.myModule = { do1: doSomeThing do2: doOtherThing }//对象 })() //IIFE do1.doSomeThing do2.doOtherThing -
模块的使用者,只需要通过模块暴露的对象调用方法来实现对应的功能
闭包的缺点及解决
- 缺点
- 函数执行完后,函数内部的局部变量没有释放,占用内存时间变长
- 容易造成内存泄漏(被占用,又用不上)
- 解决
- 能不用就不用
- 及时释放,null
1.内存溢出
- 一种程序运行出现的错误
- 当程序运行需要的内存超出了剩余的内存时,会抛出内存溢出错误
- 利用创建大数组可以快速造成内存溢出
2.内存泄漏
-
占用的内存没有及时释放
-
内存泄漏积累多了就容易导致内存溢出(内存被占用太多,可用剩余空间少)
-
常见的内存泄漏:
-
意外的全局变量
function fn() { a = 3 console.log(a) } fn() // a为全局变量,函数执行完并不会被释放,要用var定义变量! -
没有及时清理计时器(clearInterval(定时器标识))或回调函数
-
闭包 null
-
面试题
第一题
var name = "The Window"
var object = {
name: "My Object",
getNameFunc:function(){
return function(){
return this.name; //无闭包 未引用外部函数变量
};
}
};
alert(object.getNameFunc()()); //object.getNameFunc()得到的函数,函数自调用 所以是window
var name2 = "The Window"
var object2 = {
name2:"My Object",
getNameFunc:function(){
var that = this //object2
return function(){
return that.name2//object2 如果是this.name2是window跟上题一样思路
}
} //有闭包 that
}
alert(object2.getNameFunc()());//My Object
第二题
function fun (n,o) {
console.log(o)
return {
fun:function(m){
return fun(m,n)
}//n是闭包引用的变量
}
}
var a = fun(0); a.fun(1); a.fun(2); a.fun(3); //undefined 0 0 0 一直都是a的闭包,没有变量存储新闭包
var b = fun(0).fun(1).fun(2).fun(3);//undefned 0 1 2 ,fun(0)产生一个闭包 fun(0).fun(1)又是一个新的闭包,m为1传进来,return导致n=1,所以输出1,依次类推
var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined 0 1 1