闭包的总结|青训营笔记

80 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第14天。 本文总结了闭包函数的优缺点,以及为为何要在防抖和节流中使用闭包。

闭包

能访问到其他函数内部的变量的函数。

优点

避免了变量污染,不管外部变量如何定义,不会影响到内部变量
闭包的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中,即闭包可以“记住”诞生的环境,使得它诞生环境一直存在。
闭包的另一个用处,是封装对象的私有属性和私有方法。

写法

  1. 嵌套写法
  2. 回调写法
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函数还是闭包吗?

过程分析

  1. 全局执行上下文创建的作用域链,作用域链包含了全局变量对象
  2. books函数调用时创建作用域链,先复制上一级的作用域链,然后创建直接的活动对象AO推入当前作用域的顶端,books函数执行完毕后会把当前作用域销毁。
  3. 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\

  1. 若设置在返回函数里,则每次触发事件,新建一个timer变量,防抖和节流无效。\
  2. 若是设置一个全局time变量,debounce函数则不能复用(设置闭包可以使得debounce绑定多个事件,每个事件都有自己的time变量)