闭包
理解闭包,首先必须理解变量作用域
在JavaScript中,根据其特有的“链式作用域” , 子对象会一级一级向上寻找父对象的变量,也就是父对象的变量可以被子对象看见,反之则不成立。函数内部可以读取到全局变量因为函数可以算是全局对象的一个属性,但是函数外部却无法读取函数内部声明的变量。
var prop = '你能看见我';
function fun(){
var propInner = '你看不见我';
//函数内部可以看见外部变量
console.log(prop);
}
fun();//'你能看见我'
//函数外部看不见函数内部变量
console.log(propInnner)//undefined
其次,要理解函数作用域链的确定
函数作用域链在函数被声明时被确定,换句话说,函数可以"记住"自己诞生的位置,在被调用的时候会去自己诞生的位置寻找需要的变量,而不是在自己被调用的地方寻找变量
var prop = '全局作用域的变量';
function funOuter(){
var prop = '外层函数的变量';
//内层函数会在声明时确定作用域
return function funInner(){
console.log(prop);
}
}
var f = funOuter();
f();//输出‘外层函数的变量’,而不是‘全局作用域的变量’,说明函数作用域链不是在调用时确定的
最后,理解垃圾回收机制不会回收被其他函数(对象)依赖的函数(对象)
当一个内层函数在外层函数中被声明并且对外层函数的变量有所依赖时,如果我们运行外层函数将内层函数返回并使用一个变量指向返回的内层函数,那么垃圾回收机制不会回收这个被其他变量引用的内层函数,并且它与外层函数的依赖关系将导致外层函数也一值存在于内存中而不会被销毁
(垃圾回收机制会只会回收没有变量指向的对象)
结合上述三点,理解闭包及它的作用
内部函数可以一直看见外部函数的变量,所以我们可以将内部函数返回出去,让外部函数的局部变量在其函数外可见,这个被返回的内部函数就是闭包,它是连接外层函数内部与外部的桥梁,同时保证了外层函数的局部变量一直存在并且不会污染全局命名空间
function funOuter(){
var prop = '外层函数的变量'
return function funInner(){//这个就是闭包函数
console.log(prop)
}
}
console.log(prop);//输出undefined
var f = funOuter();//接收闭包使其与其所在的外部函数保存在内存中不被销毁
f();//输出'外层函数的变量'
闭包函数应用实例:防抖debounce
防抖是在用户频繁输入,但是不需要每一次输入都进行额外操作(请求数据等)时应用的函数,
比如用户在输入搜索框时自动联想可以不用在每次敲键盘都进行联想,而是在一段时间没有输入的情况下(代表用户输入完毕)进行联想
<button id = "btn">你好</button>
<script>
//停止点击按钮一段时间之后输出‘你好’
var btn = document.querySelector('#btn')
function log( e ){
console.log(e)
console.log('你好')
}
function debounce(fun , delay){
delay = delay || 1000;
/*
防抖函数,用timer记录每次延时器,时间到了没有点击就运行log函数,
时间没到时又点击就重置timer,重新计时,关键在于用防抖外层函数的变量tiemr保留上 一次定时器返回的timer值
*/
var timer = null;
//闭包函数,真正被点击后运行的就是他,他就是事件响应函数
return function funInner(){
clearTimeout(timer);
/*
事件响应函数的this为button对象,
args为mouseEvent对象,要在运行log函数时为其指定this和arguments
用一个变量记录this否则在定时器的参数函数中this为window
*/
var context = this;
var args = arguments;
timer = setTimeout(function(){
fun.apply(context , args);
} , delay)
}
}
btn.onclick = debounce(log , 1500)
</script>
参考链接:wangdoc