这是我参与「第四届青训营 」笔记创作活动的第14天。 本文总结了闭包函数的优缺点,以及为为何要在防抖和节流中使用闭包。
闭包
能访问到其他函数内部的变量的函数。
优点
避免了变量污染,不管外部变量如何定义,不会影响到内部变量
闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以“记住”诞生的环境,使得它诞生环境一直存在。
闭包的另一个用处,是封装对象的私有属性和私有方法。
写法
- 嵌套写法
- 回调写法
function fun(callback) {
var a = 1;
callback(a);
}
function callback(a) {
console.log(a + 1);
}
fun(callback); //2
console.dir(callback); // Scopes属性中只有Global
console.dir(fun); // Scopes属性中只有Global
//疑问:不是所有闭包都会保存其他函数内部的变量?
// 这段代码中,若不写fun函数,那么callback函数还是闭包吗?
过程分析
- 全局执行上下文创建的作用域链,作用域链包含了全局变量对象
- books函数调用时创建作用域链,先复制上一级的作用域链,然后创建直接的活动对象AO推入当前作用域的顶端,books函数执行完毕后会把当前作用域销毁。
- bag函数调用创建作用域,首先复制上层作用域,然后创建活动对象AO推入当前作用域顶端,bag函数执行完后当前作用域会被销毁,但是保存的books中的作用域和全局作用域不会被销毁。
面试题
function fn(){
var arr = [];
for(var i=0;i<10;i++){
arr[i] = function(){
return i;
}
}
i++;
return arr;
}
var arr = fn();// arr数组中,保存的函数中的变量i,是fn中的变量i
console.log(arr[0]()); // 11
// 改写fn,使得arr[0]()=0;
// 也可以使用es6中的块级作用域来实现。
function fn1(){
var arr = [];
for(var i=0;i<10;i++){
arr[i] = (function(i){
return function(){
i;
};
})(i) //把i作为自调用函数的参数传入进去,使得在每个自定义函数中创建一个新的名为i的变量
}
i++;
return arr;
}
var arr1 = fn1();// arr数组中,保存的函数中的变量i,是fn中的变量i
console.log(arr[0]()); // 0
你会在什么情况下使用闭包
1.防抖、节流
为什么要用闭包实现防抖和节流?
若不用闭包,1.time要么设置在返回函数里,2.要么设置为全局变量。
当设置<button onclick="foo()">foo按钮</button>
在foo = debounce(fn, wait)中
foo是debounce返回的函数,debounce只在定义foo时调用了一次,之后触发事件执行的是debounce的返回函数
这样将time变量放入debounce中,实现闭包,不会每次调用foo都新建一个time\
- 若设置在返回函数里,则每次触发事件,新建一个timer变量,防抖和节流无效。\
- 若是设置一个全局time变量,debounce函数则不能复用(设置闭包可以使得debounce绑定多个事件,每个事件都有自己的time变量)