-
Notifications
You must be signed in to change notification settings - Fork 62
/
Copy pathfunctions.js
executable file
·188 lines (158 loc) · 7.31 KB
/
functions.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
(function () {
describe('Hack Reactor Scopes Exercises', function () {
var ACTUAL;
// This resets the value of ACTUAL (to null) before each test is run
beforeEach(function () {
ACTUAL = null;
});
it('a function has access to its own local scope variables', function () {
var fn = function () {
var name = 'inner';
ACTUAL = name;
};
fn();
// This test should already be passing
expect(ACTUAL === 'inner').to.be.true;
});
// BASIC REQUIREMENTS:
it('inputs to a function are treated as local scope variables', function () {
var fn = function (name) {
ACTUAL = name;
};
fn('inner');
expect(ACTUAL === '???').to.be.true;
});
it('a function has access to the variables contained within the same scope that function was created in', function () {
var name = 'outer';
var fn = function () {
ACTUAL = name;
};
fn();
expect(ACTUAL === '???').to.be.true;
});
it('a function\'s local scope variables are not available anywhere outside that function', function () {
var firstFn = function () {
var localToFirstFn = 'inner';
};
firstFn();
expect(function () {
ACTUAL = localToFirstFn;
}).to.throw();
expect(ACTUAL === '???').to.be.true;
});
it('a function\'s local scope variables are not available anywhere outside that function, regardless of the context it\'s called in', function () {
var firstFn = function () {
var localToFirstFn = 'first';
// Although false, it might seem reasonable to think that the secondFn (which mentions the localToFirstFn variable), should have access to the localToFirstFn variable, since it's being called here from within the scope where that variable is declared.
secondFn();
};
var secondFn = function () {
ACTUAL = localToFirstFn;
};
expect(function () {
// of course, calling the secondFn should throw an error in this case, since secondFn does not have access to the localToFirstFn variable
secondFn();
}).to.throw();
expect(function () {
// in addition, calling the firstFn (which in turn calls the secondFn) should also throw, since it the calling context of secondFn has no influence over its scope access rules
firstFn();
}).to.throw();
expect(ACTUAL === '???').to.be.true;
});
it('if an inner and an outer variable share the same name, and the name is referenced in the inner scope, the inner scope variable masks the variable from the outer scope with the same name. This renders the outer scope variables inaccassible from anywhere within the inner function block', function () {
var sameName = 'outer';
var fn = function () {
var sameName = 'inner';
ACTUAL = sameName;
};
fn();
expect(ACTUAL === '???').to.be.true;
});
it('if an inner and an outer variable share the same name, and the name is referenced in the outer scope, the outer value binding will be used', function () {
var sameName = 'outer';
var fn = function () {
var sameName = 'inner';
};
fn();
ACTUAL = sameName;
expect(ACTUAL === '???').to.be.true;
});
it('a new variable scope is created for every call to a function, as exemplified with a counter', function () {
var fn = function () {
// the `||` symbol here is being used to set a default value for innerCounter. If innerCounter already contains a truthy value, then the value in that variable will be unchanged. If it is falsey however (such as if it were completely uninitialized), then this line will set it to the default value of 10.
var innerCounter = innerCounter || 10;
innerCounter = innerCounter + 1;
ACTUAL = innerCounter;
};
fn();
expect(ACTUAL === '???').to.be.true;
fn();
expect(ACTUAL === '???').to.be.true;
});
it('a new variable scope is created for each call to a function, as exemplified with uninitialized string variable', function () {
// this is a longer form of the same observation as above, using strings in stead of numbers.
var fn = function () {
var localVariable;
if (localVariable === undefined) {
// the variable will be initialized for the first time during this call to fn
ACTUAL = 'alpha';
} else if (localVariable === 'initialized') {
// the variable has already been initialized by a previous call to fn
ACTUAL = 'omega';
}
// now that actual has been set, we will initialize localVariable to refer to a string
localVariable = 'initialized';
};
fn();
expect(ACTUAL === '???').to.be.true;
fn();
expect(ACTUAL === '???').to.be.true;
});
it('an inner function can access both its local scope variables and variables in its containing scope, provided the variables have different names', function () {
var outerName = 'outer';
var fn = function () {
var innerName = 'inner';
ACTUAL = innerName + outerName;
};
fn();
expect(ACTUAL === '???').to.be.true;
});
it('between calls to an inner function, that inner function retains access to a variable in an outer scope. Modifying those variables has a lasting effect between calls to the inner function.', function () {
var outerCounter = 10;
var fn = function () {
outerCounter = outerCounter + 1;
ACTUAL = outerCounter;
};
fn();
expect(ACTUAL === '???').to.be.true;
fn();
expect(ACTUAL === '???').to.be.true;
});
// ADVANCED PRACTICE:
it('the rule about retaining access to variables from an outer scope still applies, even after the outer function call (that created the outer scope) has returned', function () {
var outerFn = function () {
// NOTE: the contents of this function is the same as the entire body of the previous test
var counterInOuterScope = 10;
var innerIncrementingFn = function () {
counterInOuterScope = counterInOuterScope + 1;
ACTUAL = counterInOuterScope;
};
innerIncrementingFn();
expect(ACTUAL === '???').to.be.true;
innerIncrementingFn();
expect(ACTUAL === '???').to.be.true;
// Here, we retain a reference to the newly created inner function for later, by assigning it to the global scope (window)
window.retainedInnerFn = innerIncrementingFn; // typeof retainedInnerFn === 'function'
};
// before we run outerFn, there will be no innerFn exported to the global scope
expect(window.retainedInnerFn).to.equal.undefined;
// running this outer function should have the same effect as running the whole previous test, with the addition of placing the innerFn somewhere that we can reach it after outerFn has returned
outerFn();
expect(window.retainedInnerFn).to.be.a('function');
// even though the outerFn has returned once the only call to it was completed a couple of lines above, the inner function remains available in the global scope, and still has access to the variables of that containing scope where it was first created.
window.retainedInnerFn();
expect(ACTUAL === '???').to.be.true;
outerFn();
});
});
})();