-
Notifications
You must be signed in to change notification settings - Fork 8
/
Closures and References.js
252 lines (139 loc) · 6.11 KB
/
Closures and References.js
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
// One of JavaScript's most powerful features is the availability of closures.
// With closures, scopes always keep access to the outer scope, in which they were defined. Since the only scoping that JavaScript has is function scope,
// all functions, by default, act as closures.
// Emulating private variables
// function Counter(start) {
// var count = start;
// return {
// increment: function() {
// count++;
// },
// get: function() {
// return count;
// }
// }
// }
// var foo = Counter(4);
// console.log(foo);
// Here, Counter returns two closures: the function increment as well as the function get.
// Both of these functions keep a reference to the scope of Counter and, therefore, always keep access to the count variable that was defined in that scope.
// foo.increment();
// foo.get(); // 5
var myfunction = adder(5);
function adder(passed_value){
var x = passed_value;
return {
increment:function(number){
x = x + number;
console.log(x);
}
}
}
myfunction.increment(10);
myfunction.increment(20);
// console.log(foo.get());
// Why Private Variables Work
// Since it is not possible to reference or assign scopes in JavaScript, there is no way of accessing the variable count from the outside. The only way to
// interact with it is via the two closures.
// var foo = new Counter(4);
// foo.hack = function() {
// count = 1337;
// };
// The above code will not change the variable count in the scope of Counter, since foo.hack was not defined in that scope. It will instead create - or override the global variable count.
// Closures Inside Loops
// One often made mistake is to use closures inside of loops, as if they were copying the value of the loop's index variable.
for(var i = 0; i < 9; i++) {
setTimeout(function() {
// console.log(i); // It will print 10 9 times.
}, 1000);
}
// The above will not output the numbers 0 through 9, but will simply print the number 10 ten times.
// The anonymous function keeps a reference to i. At the time console.log gets called,the for loop has already finished,and the value of i has been set to 10.
// In order to get the desired behavior, it is necessary to create a copy of the value of i.
// Avoiding the Reference Problem
// In order to copy the value of the loop's index variable, it is best to use an anonymous wrapper.
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
// console.log(e);
}, 1000);
})(i);
}
// The anonymous outer function gets called immediately with i as its first argument and will receive a copy of the value of i as its parameter e.
// The anonymous function that gets passed to setTimeout now has a reference to e, whose value does not get changed by the loop.
// There is another possible way of achieving this, which is to return a function from the anonymous wrapper that will then have the same behavior as the
// code above.
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
// console.log(e);
}
})(i), 1000)
}
for(var i = 0; i < 10; i++) {
setTimeout(function(e) {
// console.log(e);
}, 1000, i);
}
// Some legacy JS environments (Internet Explorer 9 & below) do not support this.
// There's yet another way to accomplish this by using .bind, which can bind a this context and arguments to function. It behaves identically to the code
// above
for(var i = 0; i < 10; i++) {
// setTimeout(console.log.bind(console, i), 1000);
}
// To better Understand CLosure let's take an example !
// A Counter Dilemma
// Suppose you want to use a variable for counting something, and you want this counter to be available to all functions.
// You could use a global variable, and a function to increase the counter:
// var counter = 0;
// function add() {
// counter += 1;
// }
// add();
// add();
// add();
// the counter is now equal to 3
// The counter should only be changed by the add() function.
// The problem is, that any script on the page can change the counter, without calling add().
// If I declare the counter inside the function, nobody will be able to change it without calling add():
// function add() {
// var counter = 0;
// counter += 1;
// }
// add();
// add();
// add();
// the counter should now be 3, but it does not work !
// It did not work! Every time I call the add() function, the counter is set to 1.
// A JavaScript inner function can solve this.
// JavaScript Nested Functions
// All functions have access to the global scope.
// In fact, in JavaScript, all functions have access to the scope "above" them.
// JavaScript supports nested functions. Nested functions have access to the scope "above" them.
// In this example, the inner function plus() has access to the counter variable in the parent function:
function add() {
var counter = 0;
function plus() {counter += 1;}
plus();
return counter;
}
// This could have solved the counter dilemma, if we could reach the plus() function from the outside.
// We also need to find a way to execute counter = 0 only once.
// We need a closure.
// JavaScript Closures
// Remember self-invoking functions? What does this function do?
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
console.log(add);
add();
add();
console.log(add());
// Example Explained
// The variable add is assigned the return value of a self-invoking function.
// The self-invoking function only runs once. It sets the counter to zero (0), and returns a function expression.
// This way add becomes a function. The "wonderful" part is that it can access the counter in the parent scope.
// This is called a JavaScript closure. It makes it possible for a function to have "private" variables.
// The counter is protected by the scope of the anonymous function, and can only be changed using the add function.
// A closure is a function having access to the parent scope, even after the parent function has closed.