You get a bonus - 1 coin for daily activity.
Now you have 1 coin
Data storage in the circuit modules
Lecture
Data for the counter
Counter object
Counter object + function
Design Reception "Module"
Understanding Closure Tasks
Closures can be used in hundreds of ways. Sometimes people themselves do not notice that they used closures - it is so simple and natural.
In this chapter, we will look at examples of using closures for storing data and tasks on this topic.
Data for the counter
In the example below, makeCounter creates a function that counts its calls:
01
functionmakeCounter() {
02
varcurrentCount = 0;
03
04
returnfunction() {
05
currentCount++;
06
returncurrentCount;
07
};
08
}
09
10
varcounter = makeCounter();
11
12
// каждый вызов увеличивает счётчик
13
counter();
14
counter();
15
alert( counter() );// 3
The current number of calls is currentCount in the currentCount variable of the external function.
At the same time, since every call to makeCounter creates a new variable object, all the created function counters are mutually independent.
1
varc1 = makeCounter();
2
3
varc2 = makeCounter();
4
5
alert( c1() );// 1
6
alert( c2() );// 1, счётчики независимы
Add an argument to the counter, which, if passed, sets the value to:
01
functionmakeCounter() {
02
varcurrentCount = 0;
03
04
returnfunction(newCount) {
05
if(newCount !==undefined) {// есть аргумент?
06
currentCount = +newCount;// сделаем его новым значением счётчика
07
// вернём текущее значение, счётчик всегда возвращает его (это удобно)
08
returncurrentCount;
09
}
10
11
currentCount++;
12
returncurrentCount;
13
};
14
}
15
16
varcounter = makeCounter();
17
18
alert( counter() );// 1
19
alert( counter(3) );// 3
20
alert( counter() );// 4
Here, to test the first argument, a comparison with undefined , so that the calls counter(undefined) and counter() work identically, as if there are no arguments.
Counter object
You can go ahead and return a full-fledged object with counter management functions:
getNext() - get the next value, what counter() call did before.
set(value) - set the value.
reset() - reset the counter.
01
functionmakeCounter() {
02
varcurrentCount = 0;
03
04
return{
05
getNext:function() {
06
return++currentCount;
07
},
08
09
set:function(value) {
10
currentCount = value;
11
},
12
13
reset:function() {
14
currentCount = 0;
15
}
16
};
17
}
18
19
varcounter = makeCounter();
20
21
alert( counter.getNext() );// 1
22
alert( counter.getNext() );// 2
23
24
counter.reset();
25
alert( counter.getNext() );// 1
... Now counter is an object with methods that use currentCount when working. From the outside, nothing else but through these methods, currentCount cannot be accessed, since it is a local variable.
Counter object + function
Unfortunately, the short beautiful call counter() disappeared, instead of it now counter.getNext() . But he was so short and comfortable ... So let's get him back:
01
functionmakeCounter() {
02
varcurrentCount = 0;
03
04
// возвращаемся к функции
05
functioncounter() {
06
return++currentCount;
07
}
08
09
// ...и добавляем ей методы!
10
counter.set =function(value) {
11
currentCount = value;
12
};
13
14
counter.reset =function() {
15
currentCount = 0;
16
};
17
18
returncounter;
19
}
20
21
varcounter = makeCounter();
22
23
alert( counter() );// 1
24
alert( counter() );// 2
25
26
counter.reset();
27
alert( counter() );// 1
Beautiful, isn't it?The fact that an object is a function does not at all interfere with adding to it any number of methods.
Design Reception "Module"
Receiving programming "module" has a huge number of variations.
Its goal is to declare functions so that unnecessary details of their implementations are hidden. Including: temporary variables, constants, auxiliary mini-functions, etc.
Making the code in the module includes the following steps:
A wrapper function is created that is executed “in place”:
(function() {
...
})();
Inside this function, local variables and functions are written that are not needed by the user of the module, but are needed by the module itself:
1
(function() {
2
varcount = 0;
3
4
functionhelper() { ... }
5
})();
They will only be accessible from within.
Those functions that are needed by the user are “exported” to the external scope.
If the function is one, this can be done by explicitly returning:
1
varfunc = (function() {
2
varcount = 0;
3
functionhelper() { ... }
4
5
returnfunction() { ... }
6
})();
... If there are many functions, you can assign them directly to the window :
1
(function() {
2
3
functionhelper() { ... }
4
5
window.createMenu =function() { ... };
6
window.createDialog =function() { ... };
7
8
})();
Or, better yet, return the object with them:
01
varMyLibrary = (function() {
02
03
functionhelper() { ... }
04
05
return{
06
createMenu:function() { ... },
07
createDialog:function() { ... }
08
};
09
10
})();
11
12
// использование
13
MyLibrary.createMenu();
All functions of the module will have access to other variables and internal functions through the closure.But from the outside, a programmer using a module can directly access only those that are exported.
This will hide the internal aspects of the implementation that only the developer of the module needs.
You can think of many other variations of this approach. In the end, the “module” is just a wrapper function for hiding variables.
Understanding Closures
Importance: 4
Write the sum function, which works like this: sum(a)(b) = a+b .
Yes, exactly, through double brackets (this is not a typo). For example:
sum(1)(2) = 3
sum(5)(-1) = 4
Decision
For the second brackets in the call to work, the first must return a function.
This function should know about a and be able to add a to b . Like this:
01
functionsum(a) {
02
03
returnfunction(b) {
04
returna + b;// возьмет a из внешнего LexicalEnvironment
05
};
06
07
}
08
09
alert( sum(1)(2) );
10
alert( sum(5)(-1) );
[Open task in new window]
Importance: 5
Create a filter(arr, func) function that takes an array of arr and returns a new one that includes only those arr elements for which func returns true .
Create a set of “ready-made filters”: inBetween(a,b) - “between a, b”, inArray([...]) - “in an array [...] ”.
Usage should be:
filter(arr, inBetween(3,6)) - selects only numbers from 3 to 6,
filter(arr, inArray([1,2,3])) - selects only elements that match one of the array values.
In some programming languages, there is an object "string buffer" that accumulates values within itself. Its functionality consists of two possibilities:
Add value to buffer.
Get current content.
The task is to implement a string buffer on functions in JavaScript, with the following syntax:
Creating an object: var buffer = makeBuffer(); .
The makeBuffer call should return a buffer function that, when calling buffer(value) adds a value to some internal storage, and when called without buffer() arguments, returns it.
Here is an example of work:
01
functionmakeBuffer() {/* ваш код */}
02
03
varbuffer = makeBuffer();
04
05
// добавить значения к буферу
06
buffer('Замыкания');
07
buffer(' Использовать');
08
buffer(' Нужно!');
09
10
// получить текущее значение
11
alert( buffer() );// Замыкания Использовать Нужно!
The buffer must convert all data to string type:
1
varbuffer = makeBuffer();
2
buffer(0); buffer(1); buffer(0);
3
4
alert( buffer() );// '010'
The solution should not use global variables.
Decision
The current value of the text is conveniently stored in the closure, in the local variable makeBuffer :
01
functionmakeBuffer() {
02
vartext ='';
03
04
returnfunction(piece) {
05
if(piece ===undefined) {// вызов без аргументов
06
returntext;
07
}
08
text += piece;
09
};
10
};
11
12
varbuffer = makeBuffer();
13
14
// добавить значения к буферу
15
buffer('Замыкания');
16
buffer(' Использовать');
17
buffer(' Нужно!');
18
alert( buffer() );// 'Замыкания Использовать Нужно!'
19
20
varbuffer2 = makeBuffer();
21
buffer2(0); buffer2(1); buffer2(0);
22
23
alert( buffer2() );// '010'
The initial value of text = '' is an empty string. Therefore, the operation text += piece adds piece to the string, automatically converting it to string type, as required in the condition.
[Open task in new window]
Importance: 5
Add a buffer from the Problem function solution — a string buffer buffer.clear() method that will clear the current buffer contents:
01
functionmakeBuffer() {
02
...ваш код...
03
}
04
05
varbuffer = makeBuffer();
06
07
buffer("Тест");
08
buffer(" тебя не съест ");
09
alert( buffer() );// Тест тебя не съест
10
11
buffer.clear();
12
13
alert( buffer() );// ""
Decision
01
functionmakeBuffer() {
02
vartext ='';
03
04
functionbuffer(piece) {
05
if(piece ===undefined) {// вызов без аргументов
06
returntext;
07
}
08
text += piece;
09
};
10
11
buffer.clear =function() {
12
text ="";
13
}
14
15
returnbuffer;
16
};
17
18
varbuffer = makeBuffer();
19
20
buffer("Тест");
21
buffer(" тебя не съест ");
22
alert( buffer() );// Тест тебя не съест
23
24
buffer.clear();
25
26
alert( buffer() );// ""
[Open task in new window]
Importance: 5
Will access to the name variable via the closure in the example below work?
Will the name variable be deleted from memory when you delete donkey.sayHi ? If not, can I somehow contact the name after removing donkey.sayHi ?
And if you assign donkey.sayHi = donkey.yell = null - will the name remain in memory?
01
varmakeDonkey =function() {
02
varname ="Ослик Иа";
03
04
return{
05
sayHi:function() {
06
alert(name);
07
},
08
yell:function() {
09
alert('И-а, и-а!');
10
}
11
};
12
}
13
14
vardonkey = makeDonkey();
15
donkey.sayHi();
Decision
Yes, it will work, thanks to the [[Scope]] link to an external variable object, which will be assigned to the sayHi and yell when creating an object.
No, the name will not be removed from memory, because despite the fact that sayHi is no longer present, there is another function yell , which also refers to an external variable object. This object is stored entirely, along with all properties.
At the same time, since the sayHi function has sayHi removed from the object and there are no references to it, there is no one else to access the name variable. It turned out that she was “stuck” in the memory, although, in fact, nobody needs it.
If both sayHi and yell deleted, then, since there are no more internal functions left, the variable object will be deleted along with the name .
[Open task in new window]
Importance: 5
The following code creates an array of shooter-function shooters . According to the plan, each shooter must display his number:
01
functionmakeArmy() {
02
03
varshooters = [];
04
05
for(vari=0; i<10; i++) {
06
varshooter =function() {// функция-стрелок
07
alert(i);// выводит свой номер
08
};
09
shooters.push(shooter);
10
}
11
12
returnshooters;
13
}
14
15
vararmy = makeArmy();
16
17
army[0]();// стрелок выводит 10, а должен 0
18
army[5]();// стрелок выводит 10...
19
// .. все стрелки выводят 10 вместо 0,1,2...9
Why do all shooters display the same thing? Correct the code so that the arrows work as intended. Offer several fixes.
Decision
What happens in this code
The makeArmy function does the following:
Creates an empty shooter array:
varshooters = [];
In the loop, it fills the array with elements via shooter.push .
In addition, each element of the array is a function, so that after the loop the array will be like this:
01
shooters = [
02
function() { alert(i); },() { alert(i); },
03
function() { alert(i); },() { alert(i); },
04
function() { alert(i); },() { alert(i); },
05
function() { alert(i); },() { alert(i); },
06
function() { alert(i); },() { alert(i); },
07
function() { alert(i); },() { alert(i); },
08
function() { alert(i); },() { alert(i); },
09
function() { alert(i); },() { alert(i); },
10
function() { alert(i); },() { alert(i); },
11
function() { alert(i); }() { alert(i); }
12
];
This array is returned from the function.
A call to the army[5]() is the receipt of an array element (it will be a function) at position 5 , and right there it is launched.
Why a mistake
First, let's see why all the arrows display the same value.
In the shooter functions, the variable i missing. When such a function is called, then i takes it from an external LexicalEnvironment ...
What will be the value of i ?
By the time the army[0]() call, the makeArmy function makeArmy already completed its work. The cycle is completed, the last value was i=10 .
As a result, all functions of the shooter receive the same thing, the last, i=10 value.
Try to fix the problem yourself.
Correction (3 options)
There are several ways to remedy the situation.
The first way to fix the code is to bind the value directly to the arrow function:
01
functionmakeArmy() {
02
03
varshooters = [];
04
05
for(vari=0; i<10; i++) {
06
07
varshooter =functionme() {
08
alert( me.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
In this case, each function stores its own number.
By the way, pay attention to the use of Named Function Expression, here in this section:
1
...
2
varshooter =functionme() {
3
alert( me.i );
4
};
5
...
If you remove the name of me and leave the appeal through the shooter , then it will not work:
1
for(vari=0; i<10; i++) {
2
varshooter =function() {
3
alert(shooter.i);// вывести свой номер (не работает!)
4
// потому что откуда функция возьмёт переменную shooter?
5
// ..правильно, из внешнего объекта, а там она одна на всех
6
};
7
shooter.i = i;
8
shooters.push(shooter);
9
}
Calling alert(shooter.i) when calling will look for the variable shooter , and this variable changes the value during the cycle, and by the time of the call it is equal to the last function created in the cycle.
If you use the Named Function Expression, then the name is strictly bound to a specific function, and therefore in the code above me.i returns the correct i .
Another more advanced solution is to use an additional function to “catch” the current value of i :
01
functionmakeArmy() {
02
03
varshooters = [];
04
05
for(vari=0; i<10; i++) {
06
07
varshooter = (function(x) {
08
09
returnfunction() {
10
alert( x );
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 look at the selected fragment more closely in order to understand what is happening:
1
varshooter = (function(x) {
2
returnfunction() {
3
alert( x );
4
};
5
})(i);
The shooter function is created as the result of a call to the intermediate functional expression function(x) , which is declared - and immediately executed, getting x = i .
Since function(x) ends right there, the value of x no longer changes. It will be used in the return function-arrow.
For beauty, you can change the name of the variable x to i , the essence of what is happening will not change:
1
varshooter = (function(i) {
2
returnfunction() {
3
alert( i );
4
};
5
})(i);
By the way, pay attention - brackets around function(i) not needed , it is possible and so:
1
varshooter =function(i) {// без скобок вокруг function(i)
2
returnfunction() {
3
alert( i );
4
};
5
}(i);
The brackets are added to the code for better readability so that the person who views it does not think that var shooter = function , but realized that this is a call "in place" and its result is assigned.
Another fun way is to wrap the entire cycle in a temporary function :
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 call (function(i) { ... }) wrapped in parentheses, so that the interpreter understands that it is a Function Expression .
The advantage of this method is more readability. In fact, we do not change the creation of the shooter , but simply wrap the iteration into a function.
[Open task
Comments
To leave a comment
If you have any suggestion, idea, thanks or comment,
feel free to write. We really value feedback and are glad to hear your opinion.
Comments
To leave a comment
Scripting client side JavaScript, jqvery, BackBone
Terms: Scripting client side JavaScript, jqvery, BackBone