// A simple example where bar has access to the lexical scope of foo even 
// though it is executed in outer scope, this is because bar() has closure
// over the lexical scope of foo().
function foo() {
	var a = 2;

	function bar() {
		console.log(a);
	}

	bar();
}

foo(); // 2
// A little more specific example which puts full light on closure. The function
// foo() returns function object, which we assign to a variable
// baz and then we execute it

function foo() {
	var a = 2;
	
	function bar() {
		console.log(a);
	}

	return bar;
}

// Here we execute a reference copy of foo, not foo itself and we still
// get the lexical access 
var baz = foo(); 

baz(); // 2
// An example where we are indirectly passing around the function as value and
// executing it at a different place in code

var fn;

function foo()  {
 var a = 2;
 function baz() {
	console.log( a );
 }
 fn = baz; // assign baz to global variable
}

function bar() {
	fn();
}

foo();

bar(); // 2
for (var i=1; i<=5; i++) {
	setTimeout( function timer() {
		console.log(i);
	}, i*1000);
}

// While it is tempting to think that this code will output 1, 2, ..5 with
// one second interval but this is not true, the output will be 6, 6, 6, .. 6
// five times with one second interval each.

// Explanation: The terminating condition of loop is when i is 6, thus 6 is
// printed, at each iteration the loop doesn't have its own copy of i, but the 
// global i instead which is accessed by each iteration. They are closed over
// the same shared global scope.

// We can use IIFE to fix this issue, because we can create a new scope for each
// iteration using IIFE
for(var i=1; i<=5; i++) {
	(function() {
		var j = i;
		setTimeout( function timer() {
			console.log(j);
		}, j*1000);
	})();
}

// Best solution is to use let
for(var i=1; i<=5; i++) {
	let j=i;
	setTimeout( function timer() {
		console.log(j);
	}, j*1000);
}

// A exceptional short form is also available to use `let` directly in the
// head of for loop
for(let i=1; i<=5; i++) {
	setTimeout( function timer() {
		console.log(i);
	}, i*1000);
}
*bar.js*
function hello(who) {
	return "Hi " + who;
}
export hello;

*foo.js*
// import only `hello()` from "bar" module
import hello from "bar";
var hungry = "hippo";

function capitalize() {
	console.log(hello(hungry).toUpperCase());
}
export capitalize;

*baz.js*
// import the entire "foo" and "bar" modules
**module foo from "foo";
module bar from "bar";

console.log(bar.hello("rhino")); // Hi rhino

foo.capitalize(); // HI HIPPO