-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclosure-scope-hoisting.js
195 lines (173 loc) · 8.5 KB
/
closure-scope-hoisting.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
{ // What is closure, how it works, where it is used?
/*
https://javascript.info/closure
http://www.jibbering.com/faq/notes/closures/
https://medium.com/better-programming/a-basic-interview-question-can-you-explain-what-a-closure-is-710b75384d48
https://tc39.es/ecma262/#sec-lexical-environments - EcmaScript Lexical Environmenr details
https://www.idontknowjavascript.com/2019/05/closure-in-javascript.html - explanation & questions
A closure is a function bundled with its lexical scope. Each func has hidden [[Environment]] object. It has
- Environment Record obj with local variables as props and other info like the value of 'this'
- reference to outer lexical environment
A Lexical Environment object dies when it becomes unreachable (just like any other object).
In other words, it exists only while there’s at least one nested function referencing it.
// script for testing in chrome, see closures in a dev tools
var g_val = 1;
var a = (a_val) => {
debugger; return b = (b_val) => {
debugger; return c = (c_val) => {
debugger; return a_val+c_val;
}
}
};
const f = () => {
let value = 123;
return () => {alert(value);}
}
let g = f(); // while g function exists, the value stays in memory
g = null; // ...and now the memory is cleaned up
// Closure is an expression (typically a function) that have access to variables in the outer (enclosing) function’s scope chain.
// It is formed when one of inner functions is made accessible outside of the outer function, so that it may be
// executed after the outer function has returned. Reference to such inner func creates new execution context.
// Closure is the combination of a function bundled together (enclosed) with references to its surrounding state
// (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner
// function. In JavaScript, closures are created every time a function is created, at function creation time. // MDN
// A closure is a function, along with all variables or functions that were in-scope at the time that the closure was
// created. In JavaScript, a closure is implemented as an “inner function”; i.e., a function defined within the body
// of another function. // TopTal
// Closures used for encapsulation, to separate lexical scopes in loops, timers, DOM event listeners
// Closure has three scopes: local, outer f-tions, global
// Anonymous Functions and IIFE’s are NOT closures (but may contain closures)
// Where it is used?
// loops
// callbacks
// higher order functions
// in modules with public API (privilaged methods), where implementation is hidden, and only interface exposed.
// in timers
// when associating Functions with object instance methods (DOM elements with obj instances)
// in stateful functions, where return values may be changed by internal state: const secret = msg => () => msg;
// for Partial Applied function & curring (apply function to some of its arguments)
/*
const partialApply = (fn, ...fixedArgs) => {
return function (...remainingArgs) {
return fn.apply(this, fixedArgs.concat(remainingArgs));
};
};
const add = (a, b) => a + b;
const add10 = partialApply(add, 10);
add10(5);
*/
// Example:
function(){} // this is not a closure
function outer(str) { return function inner(){ return 'Hi, ' + str }} //this is not a closure, but contains a closure
var notAClosure = a => b => a + b; // this is not a closure, but contains a closure. Stateful funtion.
;(() => {var a = 1; return {closureFunc: 2 + a}})() //this is not a closure, but contains a closure
};
/////////////////////////////////////////////////////////////////////////
// what is scope? describe it.
/*
Scope is a surrounding state. There is a global scope and local scope.
There are two local scopes in JS: function scope function(){const a = 1} and block scope {const a = 1}
Local/Global scope are not the same as Inner/Outer scope (it describe a scope relative to particular one).
Local Scope has access to its scope, chain of outer local scopes & a global scope.
*/
/////////////////////////////////////////////////////////////////////////
// what is Module Pattern and why to use it?
/*
Mudule Pattern is a Design Pattern which is used to wrap a set of variables and functions into single scope.
Purpose:
- create onw library
- encapsulate private internals into internal scope,
- expose public API,
- avoid name clashes with other modules/libs,
- avoid errors when composed with other libs (;)
- provide abstractions, maintainability, reusability,
Example:
- (function(){...})()
- var myLib = (function(){ return{a: 1 }})()
brackets around f are not mandatory in this case because it is inside expression =, but better still
to have it so it will be visible right away that it is not just a simple assignment operation
Details:
- IIFE used in this pattern to run function right away and save data somewhere only once (singleton)
- we need outer brackets () to make Functions Expression but not Function Declaration
- both approaches works (f)() = (f()). Syntatical difference only. Default useage is (f()).
- can return API object or create global API object
*/
///////////////////////////////////////////////////////////////////////// scope
// what output and why?
var y = 1;
if (function F(){}) { y += typeof F }
console.log(y); // 1function
///////////////////////////////////////////////////////////////////////// hoisting & scope
// what output and why?
(function () {
try {
throw new Error();
} catch (x /* inner scope */) {
var x = 1, y = 2; // hoisted to beginning of a func as undefined, inner 'x' is redefined, hoisted 'y' is redefined
console.log(x); // 1 - from inner scope
}
console.log(x); // undefined - from func outer scope
console.log(y); // 2 - from func outer scope scope
})();
///////////////////////////////////////////////////////////////////////// hoisting & scope
// what output and why?
var xz = 2;
(function () {
console.log(xz); // 'xz' is hoisted on the beginning of a func with 'undefined' value
var xz = 5;
console.log(xz); // 5
})()
///////////////////////////////////////////////////////////////////////// hoisting & scope
// what output and why?
var xz = 2;
(function b() {
console.log(xz); // ƒ xz() {} - because of hoisting
xz = 10;
return;
function xz() {}
})()
console.log(xz); // 2
///////////////////////////////////////////////////////////////////////// hoisting & scope
// what output and why?
var b = 1;
(function outer(){
var b = 2
(function inner(){
b++; // NaN
var b = 3; // hoisted to the beginning of inner f, but here defined as 3.
console.log(b) // 3
})()
})()
///////////////////////////////////////////////////////////////////////// hoisting & scope
// what output and why?
var foo = "Hello";
(function() {
console.log(foo + bar); // Helloundefined
var bar = ' World';
console.log(foo + bar); // Hello World
})();
console.log(foo + bar); // error -> bar is not defined (bar encapsulated in inner scope)
///////////////////////////////////////////////////////////////////////// hoisting
// if there is a difference?
var foo = function(){} // function expression, only foo=undefined is hoisted
function foo(){} // function statement, may be referenced before definition due to hoisting
///////////////////////////////////////////////////////////////////////// hoisting
// what output and why?
function foo() { return 1; }
alert(foo()); // 2, both 'foo' are hoisted, last one owerwrites.
function foo() { return 2; }
// what output and why?
var foo = function() { return 1; }
alert(foo()); // 1, func expression owerwrites hoisted values
foo = function() { return 2; }
///////////////////////////////////////////////////////////////////////// hoisting
// why wrap a file in a function block?
var myPlugin = (function($) { /* jQuery plugin code referencing $ */ } )(jQuery);
/*
This is Immediately Invoked Function Expression (IIFE). It creates a private namespace.
- to encapsulate 'private' and expose 'public' members and not to polute global manespace,
- to avoid name clashes between other libs and modules,
- short aliases for global variables,
- performance boost, because it is faster to debug with local variable then with global,
- benefits for compression (minificaiton)
*/