# 闭包

back2js

要想完全掌握闭包,一定要清楚函数作用域内存回收机制作用域继承

内部函数

# 函数作用域

back

作用域的概念,形象描述的话,可以认为它是一个封闭的空间,只允许在这个封闭的空间内进行一些操作,也将这个封闭空间称为私有作用域。在 JS 中,一个函数的执行就会在内存中创建一个私有作用域——封闭的空间
比如在函数中定义一个变量,只能在函数这个私有作用域中使用(也就是封闭空间)。只要超出了这个作用域,就找不到该变量了。
而且函数执行完成后,这个私有作用域(封闭的空间)就会销毁。有一种情况它是不会销毁的,那就是“闭包”,

# 内存回收机制

back

内存回收机制就是不在用到的内存,我们系统就自动进行回收从而清理出空间供其他程序使用。那回收的规则是什么?

closure1.jpg

内部函数引用着外部的函数的变量,外部的函数尽管执行完毕,作用域也不会销毁。从而形成了一种不销毁的私有作用域。
某一变量或者对象被引用着,因此在回收的时候不会释放它,因为被引用代表着被使用,回收器不会对正在引用的变量或对象回收的

# 作用域继承

back

所谓的作用域继承,就像是儿子可以继承父亲的财产一样。比如这里有一个大的盒子作为一个父级的作用域,然后在这个大的盒子里边放一个小的盒子,作为子作用域。我们规定可以在小盒子中获取到大盒子中的东西,大盒子不能获取小盒子里的东西就称为作用域继承。

函数执行,形成一个私有的作用域,保护里边的私有变量不受外界的干扰,除了保护私有变量外,还可以存储一些内容,这样的模式叫做闭包

# 闭包的作用是什么

back

保护和保存。

1、提供私有的全局变量
2、在函数调用之间保存变量(状态)

# 保护作用

back

团队开发时,每个开发者把自己的代码放在一个私有的作用域中,防止相互之间的变量命名冲突;把需要提供给别人的方法,通过 return 或 window.xxx 的方式暴露在全局下

function addToArr(element) {
  var arr = [];
  arr.push(element);
  return element + " added to " + arr;
}
1
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
1
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' ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 保存作用

back

选项卡闭包的解决方案。我们经常在网页中使用选项卡,但是它存在一个问题,那就是索引引发的问题,其实和下边的经典面试题问题相同。

# 闭包使用

back

闭包是一个重要的 JS 模式,可以私有访问变量

看实例:createGreeter返回一个匿名函数,这个函数可以访问参数 greeting。在后续的调用中,sayHello 有权访问这个 greeting 参数。

function createGreeter(greeting) {
 return function(name) {
 console.log(greeting + ', ' + name);
 }
}
const sayHello = createGreeter('你好');
sayHello('小智'); // 你好, 小智
1
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' });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19