部分概念内容来源于《JavaScript高级程序设计(第3版)》
1.概念
闭包是指有权访问另一个函数作用域中的变量的函数。【说白了,闭包就是一种特殊的函数】创建闭包的常见方式,就是在一个函数内部创建另一个函数。
一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。但是闭包的情况又有所不同。
2.实例
这里举一个程序防抖的例子,之所以举这个例子,是因为对程序防抖程序印象比较深刻,因为按之前的理解,一个方法运行结束后,该方法内定义的变量函数就会立即被销毁。防抖程序debounce()运行结束后,里面的变量fn,delay,timer理论上都已经不存在了,如果在外部运行debounce()返回的匿名函数,就会报错,但是实际上,匿名函数在debounce()的外部依然可以使用这三个变量。
/**
* 防抖程序要考虑的两个问题:
* 1. 执行环境中this,以及接受的参数问题;
* 2. 如何判断上一个防抖函数已经执行完成;
*
* 解决方案:
* 1. 通过获取执行环境的 this 和 arguments
* 使用apply方法执行原始函数:fn.apply($this, args);
* 2. 直接清除上一个计时器clearTimeout
因为如果上一个计时器没有计时结束说明在单位 delay 时间内该函数执行的两次;
如果已经计时结束执行完,说明未多次执行该函数
*
**/
function debounce(fn, delay) {
let timer = null; // 定义一个计时器
return function() {
let $this = this; // 被设置防抖的函数的执行环境的this
let args = arguments;
if (timer) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(function() {
fn.apply($this, args);
}, delay);
};
}
3.说明
在匿名函数从debounce()中被返回后,它的作用域链被初始化为包含debounce()函数的活动对象和全局变量。这样匿名函数就可以访问在debounce()中定义的所有变量了。更重要的是,debounce()函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。换句话说,当debounce()函数返回后,其执行环境的作用域链仍然在引用这个活动对象,但它的活动对象仍然会保留在内存中;直到匿名函数被销毁后,debounce()的活动对象才会被销毁。