forked from loaialaddien/javascriptNotes
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclosure.js
More file actions
191 lines (150 loc) · 7.12 KB
/
closure.js
File metadata and controls
191 lines (150 loc) · 7.12 KB
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
/*
What is a closure?
A closure is an inner function that has access to the outer (enclosing) function’s variables—scope chain.
The closure has three scope chains:
it has access to its own scope (variables defined between its curly brackets),
it has access to the outer function’s variables,
and it has access to the global variables.
The inner function has access not only to the outer function’s variables, but also to the outer function’s parameters.
Note that the inner function cannot call the outer function’s arguments object, however,
even though it can call the outer function’s parameters directly.
*/
function showName(firstName, lastName) {
var nameIntro = "Your name is ";
// this inner function has access to the outer function's variables, including the parameter
function makeFullName() {
return nameIntro + firstName + " " + lastName;
}
return makeFullName();
}
showName("Michael", "Jackson"); // Your name is Michael Jackson
//or we can return the function itself to later be called with access to the local variables
function CreateCounter(startingValue) {
let counterStartingValue = startingValue;
return function() {
return counterStartingValue++;
}
}
let counterFromZero = CreateCounter(0);
let counterFromTen = CreateCounter(10);
counterFromZero(); //return 0
counterFromZero(); //return 1
counterFromTen(); //return 10
counterFromTen(); //return 11
//closure can also access outer Function parameters
function CreateCounter(startingValue, IsIncreasing) {
let counterStartingValue = startingValue;
return function() {
if (IsIncreasing) {
return counterStartingValue++;
} else {
return counterStartingValue--;
}
}
}
var counterToZeroFrom10 = CreateCounter(10, false);
counterToZeroFrom10();
counterToZeroFrom10();
/*
*
Closures have access to the outer function’s variable even after the outer function returns:
One of the most important and ticklish features with closures is that the inner function still
has access to the outer function’s variables even after the outer function has returned.
, they use the same scope chain that was in effect when they were created.
This means that even after the outer function has returned,
the inner function still has access to the outer function’s variables
*/
// Closures store references to the outer function’s variables;
// they do not store the actual value.
// Closures get more interesting when the value of the outer function’s variable changes before the closure is called.
// And this powerful feature can be harnessed in creative ways, such as this private variables example
// first demonstrated by Douglas Crockford:
function celebrityID() {
var celebrityID = 999;
// We are returning an object with some inner functions
// All the inner functions have access to the outer function's variables
return {
getID: function() {
// This inner function will return the UPDATED celebrityID variable
// It will return the current value of celebrityID, even after the changeTheID function changes it
return celebrityID;
},
setID: function(theNewID) {
// This inner function will change the outer function's variable anytime
celebrityID = theNewID;
}
}
}
var mjID = celebrityID(); // At this juncture, the celebrityID outer function has returned.
mjID.getID(); // 999
mjID.setID(567); // Changes the outer function's variable
mjID.getID(); // 567: It returns the updated celebrityId variable
// Closures Gone Awry
//
//Because closures have access to the updated values of the outer
//function’ s variables,
// they can also lead to bugs
// when the outer function’s variable changes with a for loop.
function celebrityIDCreator(theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
//here we don't assign the celebrity ['id'] to a value but to a function
//and since this will be called three times after the for loop finishes execution
//and i will be incremented till 3,
theCelebrities[i]["id"] = function() {
return uniqueID + i;
};
}
return theCelebrities;
}
var actionCelebs = [{ name: "Stallone", id: 0 }, { name: "Cruise", id: 0 }, { name: "Willis", id: 0 }];
var createIdForActionCelebs = celebrityIDCreator(actionCelebs);
createIdForActionCelebs[0].id() //103
createIdForActionCelebs[1].id() //103
createIdForActionCelebs[2].id() //103
/*
var stalloneID = createIdForActionCelebs [0];
//console.log(stalloneID.id()); // 103
// In the preceding example, by the time the anonymous functions are called,
//the value of i is 3 (the length of the array and then it increments).
//The number 3 was added to the uniqueID to create 103 for ALL the celebritiesID.
//So every position in the returned array get id = 103, instead of the intended 100, 101, 102.
// The reason this happened was because, as we have discussed in the previous example,
// the closure (the anonymous function in this example) has access to the outer function’s variables by reference, not by value.
// So just as the previous example showed that we can access the updated variable with the closure,
// this example similarly accessed the i variable when it was changed,
// since the outer function runs the entire for loop and returns the last value of i, which is 103.
*/
//To fix this side effect (bug) in closures, you can use an Immediately Invoked Function Expression (IIFE),
//such as the following:
function celebrityIDCreator(theCelebrities) {
var i;
var uniqueID = 100;
for (i = 0; i < theCelebrities.length; i++) {
theCelebrities[i]["id"] = function(j) { // the j parametric variable is the i passed in on invocation of this IIFE
return function() {
return uniqueID + j; // each iteration of the for loop passes the current value of i into this IIFE and it saves the correct value to the array
}() // BY adding () at the end of this function, we are executing it immediately and returning just the value of uniqueID + j, instead of returning a function.
}(i); // immediately invoke the function passing the i variable as a parameter
}
return theCelebrities;
}
var actionCelebs = [{ name: "Stallone", id: 0 }, { name: "Cruise", id: 0 }, { name: "Willis", id: 0 }];
var createIdForActionCelebs = celebrityIDCreator(actionCelebs);
var stalloneID = createIdForActionCelebs[0];
console.log(stalloneID.id); // 100
var cruiseID = createIdForActionCelebs[1];
console.log(cruiseID.id); // 101
(function(x) {
return (function(y) {
console.log(x);
console.log(y);
})(2)
})(1);
//1
/**
* in this example, since x is not defined in the inner function,
* the scope of the outer function is searched for a defined variable x,
* which is found to have a value of 1
*/