Create a function sum that will work like that: sum(a)(b) = a+b.
Yes, the syntax is dual brackets. Funny, isn’t it? For instance:
sum(1)(2) = 3
sum(5)(-1) = 4
Open solution
Create a function sum that will work like that: sum(a)(b) = a+b and accepts any number of brackets.
For instance:
1
sum(1)(2) == 3
2
sum(5)(-1)(2) == 6
3
sum(6)(-1)(-2)(-3) == 0
4
sum(0)(1)(2)(3)(4)(5) == 15
Open hint 1Open solution
Closures
Nested function may continue to live after the outer function has finished:
1
functionUser(name) {
2
3
this.say = function(phrase) {
4
alert(name + ' says: '+ phrase)
5
}
6
7
}
8
9
varuser = newUser('John')
Marking up LexicalEnvironments:
Note, the this context is not related to scopes and variables. It does not participate here.
As we see, this.say is a property in the user object, so it continues to live after User completed.
And if you remember, when this.say is created, it (as every function) gets an internal referencethis.say.[[Scope]] to current LexicalEnvironment. So, the LexicalEnvironment of the current Userexecution stays in memory. All variables of User also are it’s properties, so they are also carefully kept, not junked as usually.
The whole point is to ensure that if the inner function wants to access an outer variable in the future, it is able to do so.
The inner function keeps a reference to the outer LexicalEnvironment.
The inner function may access variables from it any time even if the outer function is finished.
The browser keeps the LexicalEnvironment and all it’s properties(variables) in memory until there is an inner function which references it.
This is called a closure.
Mutability of LexicalEnvironment
Several function may share same outer LexicalEnvironment. In this case they can modify it’s properties.
In the example below, this.fixName changes name, which is used by this.say:
Here user.fixName.[[Scope]] and user.say.[[Scope]] reference same LexicalEnvironment, which corresponds to new User run.
From (1) to (2), the LexicalEnvironment.name is updated, so both functions see the variable change.
Variables in outer LexicalEnvironment may change.
Inner functions always see the last value.
The notorious closure loop
The task below contains interesting tricks, best demonstrated by an example. Please, glance at the solution, or, much better, try to solve it.
Here is a function to create an army of shooters:
01
functionmakeArmy() {
02
03
varshooters = []
04
05
for(vari=0; i<10; i++) {
06
varshooter = function() { // a shooter is a function
07
alert(i) // which should alert it's number
08
}
09
shooters.push(shooter)
10
}
11
12
returnshooters
13
}
14
15
vararmy = makeArmy()
16
17
varshooter = army[0] // first shooter
18
shooter() // alerts 10, should be 0
19
20
shooter = army[5] // 5th shooter
21
shooter() // alerts 10, should be 5
22
23
// all shooters alert same: 10 instead of 1,2,3...10.
Why all shooters alert the same? How to make each shooter output it’s number?
Solution
Note that the shooter function. Does not have a variable named i.
So, when it is called, the interpreter takes i from the outer LexicalEnvironment.
The problem is that at the time when shooters are run, function makeArmy has already finished the execution.
The loop has finished and the variable i is now 10.
There are two possible solutions.
The first one is to put the correct value into the shooting function itself.
01
functionmakeArmy() {
02
03
varshooters = []
04
05
for(vari=0; i<10; i++) {
06
07
varshooter = function() {
08
alert( arguments.callee.i )
09
}
10
shooter.i = i
11
12
shooters.push(shooter)
13
}
14
15
returnshooters
16
}
17
18
vararmy = makeArmy()
19
20
army[0]() // 0
21
army[1]() // 1
Another, more advanced solution is to use an extra function to trap the current value of i:
01
functionmakeArmy() {
02
03
varshooters = []
04
05
for(vari=0; i<10; i++) {
06
07
varshooter = function(i) {
08
09
returnfunction() {
10
alert( i )
11
}
12
13
}(i)
14
15
shooters.push(shooter)
16
}
17
18
returnshooters
19
}
20
21
vararmy = makeArmy()
22
23
army[0]() // 0
24
army[1]() // 1
Let’s consider the highlighted fragment more thoroughly.
1
varshooter = function(i) {
2
returnfunction() {
3
alert( i )
4
}
5
}(i)
Here, the actual shooting function is created as the result of an anonymous function(i)which is created and executed in one place.
So, when it comes to executing alert(i), it will be taken from LexicalEnvironment of the anonymous function.
So, the anonymous function traps current i into it’s LexicalEnvironment and allows the shooter to access it.
The last way is to wrap the whole loop into temporary function. Sometimes that’s more readable:
01
functionmakeArmy() {
02
03
varshooters = []
04
05
for(vari=0; i<10; i++) (function(i) {
06
07
varshooter = function() {
08
alert( i )
09
}
10
11
shooters.push(shooter)
12
13
})(i)
14
15
returnshooters
16
}
17
18
vararmy = makeArmy()
19
20
army[0]() // 0
21
army[1]() // 1
The (function(i) { ... }) definition is wrapped into brackets to make sure the interpreter treats that as expression.
[[Scope]] for new Function
There is an exception to general scope binding rule. When you create a function using new Function, it’s[[Scope]] points to window, not to current LexicalEnvironment.
The following example demonstrates how a function, created with new Function ignores local variable aand outputs the global variable.
The regular behavior:
01
window.a = 1;
02
functiongetFunc() {
03
vara = 2;
04
05
varfunc = function() { alert(a) }
06
07
returnfunc;
08
}
09
10
getFunc()() // 2, from LexicalEnvironemnt of getFunc
And now the function, created by new Function:
1
window.a = 1
2
functiongetFunc() {
3
vara = 2
4
5
varfunc = newFunction('', 'alert(a)')
6
returnfunc
7
}
8
9
getFunc()() // 1, from window
http://javascript.info/tutorial/closures
Summary
We discussed the following topics:
How variables are handled in JavaScript.
How scopes work.
What is a closure and how to use it.
Possible pitfalls and subtles in working with closures.
Closures in JavaScript is like a salt. You can live without it, but not very long. Usually people put it everywhere…
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现