5.closure.md

Closure

闭包已经是计算机编程领域中很流行的概念。

Brendan Eich: js的作者
一开始是想把scheme放在浏览器跑的。(scheme: 一种旧的函数式编程语言)
但是网景公司想要一种类似java的语言。所以….结合两者, 出现了javascript。

其实我们都在使用闭包的概念进行编程, 像异步ajax.

What’s Closure?

Closure是函数有能力记住和访问变量的词法作用域, 在函数本身已经执行完成后。(数据持久性)

这是因为,js引擎的垃圾回收机制, 执行我们的代码的时候,js维护着一个调用栈。在函数执行完成的时候,由函数的垃圾回收机制去处理这个调用栈(调用栈内包含函数的词法作用域), 要销毁的时候,发现还存在引用。那么垃圾回机制就不处理它。这就导致这个词法作用域保留了下来,也让该函数具有了数据持久性。

注意了,学院派倾向于把闭包的概念应用在单个的变量上,以变量为单位。而在js引擎里,闭包是基于词法作用域的。因此,如果你有个变量, 存在大量的数据, 那么该变量是不会被垃圾收集机制收集的。

1
2
3
4
5
6
const timer = (text) => {
setTimeout(() => {
console.log(test)
}, 1000)
}
timer('Hello World.')
1
const printer = x => () => console.log(x);

Closure var

1
2
3
4
5
6
7
8
9
10
var teacher = 'Kyle';

var myTeacher = () => {
console.log(teacher);
// 这里的变量引用是软链接,不是自己创建一个封闭的作用域
// 它并没有把这个变量抓住, 生成一个副本
}

teacher = 'Suzy';
myTeacher(); // Suzy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
for(var i = 0; i < 4; i++) {
setTimout(() => {
console.log(i)
}, i * 1000);
}

// 解决方案1: let
for(var i = 0; i < 4; i++) {
let j = i;
setTimout(() => {
console.log(j);
}, j * 1000);
}
// 解决方案2: 匿名函数
var main = () => {
for(var i = 0; i < 4; i++) {
((i)=> { // 在setTimeout外面写一个匿名函数,新建作用域, 形成闭包变量
setTimeout(() => {
console.log(i)
}, i * 1000);
})(i);

}
}
main();
// 解决方案3: 匿名函数
var main = () => {
for(var i = 0; i < 4; i++) {
setTimeout(((i) => {
return () => {
console.log(i)
}
})(i), i * 1000);
}
}
main();

Module Pattern

要了解什么是Module, 让我们先看什么不是Module.

1
2
3
4
5
6
7
var workspace = {
name: 'Kyle',
ask (quetion) {
console.log(quetion);
}
}
// 这是命名空间,并不是模块化

模块的化的概念中有一个封装的概念, 其实指的是隐藏数据和行为, 只暴露出单一的接口供使用者使用.而封装内部的东西都是私有的,对于外部来说是不可见和不可操作了。唯一可以交流的只有暴露出来的接口。

Modules encapsulate data and behavior(Method) together.The state(data) of a module is held by its methods via closure.

1
2
3
4
5
6
7
8
9
10
11
var workspace = (function Module(teacher) {
var publicAPI = { ask };
return publicAPI;

function ask (quetion) {
console.log(teacher, quetion);
}

})('Kyle');

workspace.ask('what?');
1
2
3
4
5
6
7
8
9
10
11
12
13
 
function Module(teacher) {
var publicAPI = { ask };
return publicAPI;

function ask (quetion) {
console.log(teacher, quetion);
}

};

var workspace = Module('Kyle');
workspace.ask('what?');

在早期, js中很多框架和工具中,都使用这种方式去模块化。

module and node.js

ES6 module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// file is workspace.mjs
var teacher = 'Kyle';

// 暴露单个ask
export default function ask (quetion) {
console.log(teacher, quetion);
}

// 或者使用多个
export default {
ask,
}

// main.js
import ask from 'workspace.mjs';

ask('what?');

// 或者你可以给你模块赋予别名

import * as workspace from 'workspace.mjs';

workspace.ask('what?');