JavaScript闭包总结

什么是/如何理解JavaScript中的闭包?

A: 在JavaScript 中,根据词法作用域的规则,内部函数总是可以访问其外部函数中声明的变量,当通过调用一个外部函数返回一个内部函数后,即使该外部函数已经执行结束了,但是内部函数引用外部函数的变量依然保存在内存中。

一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。-- MDN闭包 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

词法作用域

略。

更改闭包引用的自由变量

JavaScript中,闭包函数是可以改变其引用的外部变量的,这段代码中这里还有一个值得注意的地方是JavaScript中的全局变量,局部变量与变量提升:

1
2
3
4
5
6
7
8
9
10
11
12
// var i = 8;
function outer() {
i = 9;
// var i = 9;
return function() {
i += 1
}
}
const inner = outer()
console.log("一开始的i:", i)
inner();
console.log("被内部函数操作后的i:", i)

如果全局变量跟局部变量重名,局部变量的scope会覆盖掉全局变量的scope。

闭包的回收和内存泄漏:

如果引用闭包的函数是一个全局变量,那么闭包会一直存在直到页面关闭;但如果这个闭包以后不再使用的话,就会造成内存泄漏。所以在使用闭包的时候,你要尽量注意一个原则:如果该闭包会一直使用,那么它可以作为全局变量而存在;但如果使用频率不高,而且占用内存又比较大的话,那就尽量让它成为一个局部变量。

闭包的应用场景

  1. 模拟私有变量
  2. 偏函数与柯里化

例题:输出结果是什么?

例1

1
2
3
4
5
6
7
8
9
10
11
12
var test = (function() {
var num = 0
return () => {
return num++
}
}())

for (var i = 0; i < 10; i++) {
test()
}

console.log(test())

下面这段代码中还应该注意小括号的用法
关于小括号,如果分号和括号同处上下文,常出现这样的错误,js分号的坑

1
2
3
4
5
// var x = 0
var x = 0;
(function() {
console.log(x)
})()

例2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function foo(a,b){
console.log(b);
return {
foo: function(c){
return foo(c,a);
}
}
}

var func2=foo(0).foo(1).foo(2).foo(3);
// foo(0) -> undefined {foo: function(c) {return foo(c,0)}}
// foo(0).foo(1) -> foo(1,0) -> 0 {foo: function(c) {return foo(c,1)}}
// foo(0).foo(1).foo(2) -> foo(2,1) -> 1 {foo: function(c) {return foo(c,2)}}
// foo(0).foo(1).foo(2).foo(3) -> foo(3,2) -> 2 {foo: function(c) {return foo(c,3)}}

var func3=foo(0).foo(1); // undefined {foo: function(c) {return foo(c,0)}} -> foo(1,0) -> 0 {foo: function(c) {return foo(c,1)}}
func3.foo(2); // foo(2,1) -> 1 {foo: function(c) {return foo(c,2)}}
func3.foo(3); // foo(3,2) -> 2 {foo: function(c) {return foo(c,3)}}

语言对比:Python中的闭包

1
2
3
4
5
6
7
8
9
10
11
12
def outer():
x = 1
y = [1, 2]
def inner():
# x = 2
print(x)
x += 1
# y *= 2
y.append(3)
print(y)
z = (1, 2)
z[0] = 9

Python的命名空间遵循LEGB顺序, x+=1 会导致Error UnboundLocalError: local variable 'x' referenced before assignment,使闭包无法更改引用的外部变量,这是由于 += 操作会导致解释器认为有一个并不存在的局部变量 x ;虽然在python中,闭包强调不可变和纯函数,但从另一个角度,对于 y 来说,由于它是可变对象,有直接在其上操作更改其值的可行性,而 x 是 不可变对象,无法进行任何操作(类比C中的值传递与指针传递),因此,所谓“不写”指的是不改变enclosing变量的内存地址,(python可变对象与不可变对象)函数 inner 有一个只读属性 __closure__,存储了函数所引用的自由变量 x ,y

作用域链和闭包:代码中出现相同的变量,JavaScript引擎如何选择)
变量作用域,闭包
闭包的应用场景,内存泄漏的排查手段等


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!