# 闭包
要想完全掌握闭包,一定要清楚
函数作用域、内存回收机制、作用域继承。内部函数
# 函数作用域
作用域的概念,形象描述的话,可以认为它是一个封闭的空间,只允许在这个封闭的空间内进行一些操作,也将这个封闭空间称为私有作用域。在 JS 中,一个函数的执行就会在内存中创建一个私有作用域——封闭的空间。
比如在函数中定义一个变量,只能在函数这个私有作用域中使用(也就是封闭空间)。只要超出了这个作用域,就找不到该变量了。
而且函数执行完成后,这个私有作用域(封闭的空间)就会销毁。有一种情况它是不会销毁的,那就是“闭包”,
# 内存回收机制
内存回收机制就是不在用到的内存,我们系统就自动进行回收从而清理出空间供其他程序使用。那回收的规则是什么?

内部函数引用着外部的函数的变量,外部的函数尽管执行完毕,作用域也不会销毁。从而形成了一种不销毁的私有作用域。
某一变量或者对象被引用着,因此在回收的时候不会释放它,因为被引用代表着被使用,回收器不会对正在引用的变量或对象回收的
# 作用域继承
所谓的作用域继承,就像是儿子可以继承父亲的财产一样。比如这里有一个大的盒子作为一个父级的作用域,然后在这个大的盒子里边放一个小的盒子,作为子作用域。我们规定可以在小盒子中获取到大盒子中的东西,大盒子不能获取小盒子里的东西就称为作用域继承。
函数执行,形成一个私有的作用域,保护里边的私有变量不受外界的干扰,除了保护私有变量外,还可以存储一些内容,这样的模式叫做闭包
# 闭包的作用是什么
保护和保存。
1、提供私有的全局变量
2、在函数调用之间保存变量(状态)
# 保护作用
团队开发时,每个开发者把自己的代码放在一个私有的作用域中,防止相互之间的变量命名冲突;把需要提供给别人的方法,通过 return 或 window.xxx 的方式暴露在全局下
function addToArr(element) {
var arr = [];
arr.push(element);
return element + " added to " + arr;
}
2
3
4
5
似乎合理,但结果不是咱们所期望的:
var firstPass = addToArr("a");
var secondPass = addToArr("b");
console.log(firstPass); // a added to a
console.log(secondPass); // b added to b
2
3
4
function addToArr() {
var arr = [];
return function push(element) {
arr.push(element);
console.log(arr);
};
//return element + " added to " + arr;
}
var result = addToArr();
result("a"); // [ 'a' ]
result("b"); // [ 'a', 'b' ]
2
3
4
5
6
7
8
9
10
11
12
13
14
# 保存作用
选项卡闭包的解决方案。我们经常在网页中使用选项卡,但是它存在一个问题,那就是索引引发的问题,其实和下边的经典面试题问题相同。
# 闭包使用
闭包是一个重要的 JS 模式,
可以私有访问变量
看实例:createGreeter返回一个匿名函数,这个函数可以访问参数 greeting。在后续的调用中,sayHello 有权访问这个 greeting 参数。
function createGreeter(greeting) {
return function(name) {
console.log(greeting + ', ' + name);
}
}
const sayHello = createGreeter('你好');
sayHello('小智'); // 你好, 小智
2
3
4
5
6
7
咱们可以设想一个初始函数apiConnect(apiKey),它返回一些使用API key的方法。在这种情况下,apiKey 只需要提供一次即可。
function apiConnect(apiKey) {
function get(route) {
return fetch(`${route}?key=${apiKey}`);
}
function post(route, params) {
return fetch(route, {
method: 'POST',
body: JSON.stringify(params),
headers: {
'Authorization': `Bearer ${apiKey}`
}
})
}
return { get, post }
}
const api = apiConnect('my-secret-key');
// 不再需要包含 apiKey
api.get('http://www.example.com/get-endpoint');
api.post('http://www.example.com/post-endpoint', { name: 'Joe' });
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19