作用域闭包

什么是闭包?

闭包是基于词法作用域书写代码时产生的自然结果

当函数可以记住并访问所在词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行

闭包是指有权访问另一个函数作用域中的变量的函数

1
2
3
4
5
6
7
8
9
function foo(){
var a = 2;
function bar(){
console.log(a);
}
return bar;
}
var baz = foo();
baz(); //2

为什么在外部能够访问到内部的值?

这就涉及到JS引擎的垃圾回收机制了

例如上面的例子,foo最终返回的是一个函数,然后再在外部调用bar这个函数

即在自己定义的词法作用域以外的地方执行,也正是因为bar本身要使用这个内部作用域,引擎的垃圾回收会被阻止,这也是为什么说闭包会造成性能浪费

1
2
3
4
5
6
7
8
9
10
11
12
13
var fn;
function foo(){
var a = 2;
function baz(){
console.log(a);
}
fn = baz; //fn到全局变量
}
function bar(){
fn();
}
foo();
bar(); //2

无论通过何种手段将内部函数传递到所在词法作用域以外,它都会持有对原始定义作用域的引用,无论在何处执行这个函数都会使用闭包

闭包的应用

在定时器,事件监听器、ajax请求、跨窗口通信、Web Workers或者其他任何的异步(或者同步)任务中,只要使用了回调函数,实际上就是在使用闭包

模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function CoolModule(){
var something = "cool";
var another = [1,2,3];

function doSomething(){
console.log(something);
}
function doAnother(){
console.log(another.join('!'));
}
return {
dosomething:doSomething,
doAnother:doAnother
}
}
var foo = CoolModule();
foo.doSomething(); //cool
foo.doAnother(); //1!2!3

这里返回的是一个对象字面量,保持内部数据变量隐藏且私有

实际上也可以返回一个内部函数,Jquery和$标识符就是jQuery模块的公共API

模块模式需要必备两个必要条件:

1.必须有外部的封闭函数,该函数至少被调用一次(每次调用都会创建一个新的模块实例)

2.封闭函数必须返回至少一个内部函数,这样内部函数才能在私有作用域中形成闭包,并且可以访问或修改私有的状态

上面的代码CoolModule的模块创建器,可以被任意调用多次,每次调用都会创建一个新的模块实例

如果只需要一个实例,可以对模块进行简单的改进

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var foo = (function CoolModule(){
var something = "cool";
var another = [1,2,3];

function doSomething(){
console.log(something);
}
function doAnother(){
console.log(another.join('!'));
}
return {
dosomething:doSomething,
doAnother:doAnother
}
})();
foo.doSomething(); //cool
foo.doAnother(); //1!2!3

将模块函数转换成了IIFE,立即调用这个函数并且将返回值赋值给foo

当然也可以更改模块使其接受参数

在现代模块机制中会使用import、module、export来进行模块导入

import可以将一个模块中的一个或多个API导入到当前作用域中,并绑定在一个变量上

module会将整个模块的API导入并绑定到一个变量上

export会将当前模块的一个标识符(变量、函数)导出为公共API


作用域闭包
https://blog-theta-ten.vercel.app/2021/08/30/作用域闭包/
作者
Chen
发布于
2021年8月30日
许可协议