클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(lexical environment)과의 조합이다.
무슨 의미인지 잘 와닿지 않는다.
                    
                        function outerFunc() {
                          var x = 10;
                          var innerFunc = function () { console.log(x); };
                          innerFunc();
                        }
                        outerFunc(); // 10
                    
                
                함수 outerFunc 내에서 내부함수 innerFunc가 선언되고 호출되었다. 
                    스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언하였는지에 따라 결정된다. 
                    이를 렉시컬 스코핑(Lexical scoping)라 한다. 
                    위 예제의 함수 innerFunc는 함수 outerFunc의 내부에서 선언되었기 때문에 함수 innerFunc의 상위 스코프는 함수 outerFunc이다. 
                    함수 innerFunc가 전역에 선언되었다면 함수 innerFunc의 상위 스코프는 전역 스코프가 된다.
                
                    
                        var x = 10;
                        console.log(x);     // 10
                        function xchange(){
                            var x = 5;
                            console.log(x);
                        }
                        xchange();          // 5
                        console.log(x);     // 10
                    
                
                
                    
                        var x = 10;
                        console.log(x);     // 10
                        function xchange(){
                            x = 5;
                            console.log(x);
                        }
                        xchange();          // 5
                        console.log(x);     // 5
                    
                
                
                    
                        var i = 10;
                        for(var j=0; j<20; j++){
                            i = j;
                            console.log(j);         // 1~19
                        }
                        console.log(i);             // 19
                    
                
                
                    
                        var i = 10;
                        for(var i=0; i<20; i++){
                            i = i;
                            console.log(i);         // 1~19
                        }
                        console.log(i);             // 20
                    
                
                
                    
                        function a (){
                            var x = 10;
                            var b = function(){
                                var x = 5;
                                console.log(x);
                            }
                            b();
                            console.log(x);
                        }
                        a();
                        // 5
                        // 10
                    
                
                함수 innerFunc가 함수 outerFunc의 내부에 선언된 내부함수이므로 
                    
                        function a (){
                            var x = 10;
                            var b = function(){
                                var x = 5;
                                console.log(x);
                                console.log(this);
                            }
                            b();
                            console.log(x);
                            console.log(this);
                        }
                        a();
                        // 5
                        // Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
                        // 10
                        // Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
                    
                
                내부함수 innerFunc가 자신을 포함하고 있는 외부함수 outerFunc의 변수 x에 접근할 수 있는 것, 
                    
                        var x = 10;
                        function outerFunc() {
                          var innerFunc = function () { console.log(x); };
                          innerFunc();
                        }
                        outerFunc();
                        // 10
                    
                
                이번에는 내부함수 innerFunc를 함수 outerFunc 내에서 호출하는 것이 아니라 반환하도록 변경해 보자.
                
                    
                        function outerFunc() {
                          var x = 10;
                          var innerFunc = function () { console.log(x); };
                          return innerFunc;
                        }
                        /**
                         *  함수 outerFunc를 호출하면 내부 함수 innerFunc가 반환된다.
                         *  그리고 함수 outerFunc의 실행 컨텍스트는 소멸한다.
                         */
                        var inner = outerFunc();
                        var bb = outerFunc();
                        inner();
                        // 10
                        bb();
                        // 10
                    
                
                함수 outerFunc는 내부함수 innerFunc를 반환하고 생을 마감했다. 클로저는 함수와 그 함수가 선언됐을 때의 렉시컬 환경(Lexical environment)과의 조합이다.
위 정의에서 말하는 “함수”란 반환된 내부함수를 의미하고 “그 함수가 선언될 때의 렉시컬 환경(Lexical environment)”란 내부 함수가 선언됐을 때의 스코프를 의미한다. 
            
                            
                                <!DOCTYPE html>
                                <html>
                                <body>
                                <button class="toggle">toggle</button>
                                <div class="box" style="width: 100px; height: 100px; background: red;"></div>
                                <script>
                                    var box = document.querySelector('.box');
                                    var toggleBtn = document.querySelector('.toggle');
                                    var toggle = (function () {
                                        var isShow = false;
                                        // ① 클로저를 반환
                                        return function () {
                                            box.style.display = isShow ? 'block' : 'none';
                                            // ③ 상태 변경
                                            isShow = !isShow;
                                        };
                                    })();
                                    // ② 이벤트 프로퍼티에 클로저를 할당
                                    toggleBtn.onclick = toggle;
                                </script>
                                </body>
                                </html>
                            
                        
                        
                            
                                <!DOCTYPE html>
                                <html>
                                <body>
                                <p>전역 변수를 사용한 Counting</p>
                                <button id="inclease">+</button>
                                <p id="count">0</p>
                                <script>
                                    var incleaseBtn = document.getElementById('inclease');
                                    var count = document.getElementById('count');
                                    // 카운트 상태를 유지하기 위한 전역 변수
                                    var counter = 0;
                                    function increase() {
                                        return ++counter;
                                    }
                                    incleaseBtn.onclick = function () {
                                        count.innerHTML = increase();
                                    };
                                </script>
                                </body>
                                </html>
                            
                        
                        위 코드는 잘 동작하지만 오류를 발생시킬 가능성을 내포하고 있는 좋지 않은 코드다. 
                            
                                <!DOCTYPE html>
                                <html>
                                <body>
                                <p>지역 변수를 사용한 Counting</p>
                                <button id="inclease">+</button>
                                <p id="count">0</p>
                                <script>
                                    var incleaseBtn = document.getElementById('inclease');
                                    var count = document.getElementById('count');
                                    function increase() {
                                        // 카운트 상태를 유지하기 위한 지역 변수
                                        var counter = 0;
                                        return ++counter;
                                    }
                                    incleaseBtn.onclick = function () {
                                        count.innerHTML = increase();
                                    };
                                </script>
                                </body>
                                </html>
                            
                        
                        전역변수를 지역변수로 변경하여 의도치 않은 상태 변경은 방지했다. 
                            
                                <!DOCTYPE html>
                                <html>
                                <body>
                                <p>클로저를 사용한 Counting</p>
                                <button id="inclease">+</button>
                                <p id="count">0</p>
                                <script>
                                    var incleaseBtn = document.getElementById('inclease');
                                    var count = document.getElementById('count');
                                    var increase = (function () {
                                        // 카운트 상태를 유지하기 위한 자유 변수
                                        var counter = 0;
                                        // 클로저를 반환
                                        return function () {
                                            return ++counter;
                                        };
                                    }());
                                    incleaseBtn.onclick = function () {
                                        count.innerHTML = increase();
                                    };
                                </script>
                                </body>
                                </html>
                            
                        
                        스크립트가 실행되면 즉시실행함수(immediately-invoked function expression)가 호출되고 
                            변수의 값은 누군가에 의해 언제든지 변경될 수 있어 오류 발생의 근본적 원인이 될 수 있다. 
                            상태 변경이나 가변(mutable) 데이터를 피하고 불변성(Immutability)을 지향하는 함수형 프로그래밍에서 
                            부수 효과(Side effect)를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이기 위해 클로저는 적극적으로 사용된다.
                        
                            
                                // 함수를 인자로 전달받고 함수를 반환하는 고차 함수
                                // 이 함수가 반환하는 함수는 클로저로서 카운트 상태를 유지하기 위한 자유 변수 counter을 기억한다.
                                function makeCounter(predicate) {
                                  // 카운트 상태를 유지하기 위한 자유 변수
                                  var counter = 0;
                                  // 클로저를 반환
                                  return function () {
                                    counter = predicate(counter);
                                    return counter;
                                  };
                                }
                                // 보조 함수
                                function increase(n) {
                                  return ++n;
                                }
                                // 보조 함수
                                function decrease(n) {
                                  return --n;
                                }
                                // 함수로 함수를 생성한다.
                                // makeCounter 함수는 보조 함수를 인자로 전달받아 함수를 반환한다
                                const increaser = makeCounter(increase);
                                console.log(increaser()); // 1
                                console.log(increaser()); // 2
                                // increaser 함수와는 별개의 독립된 렉시컬 환경을 갖기 때문에 카운터 상태가 연동하지 않는다.
                                const decreaser = makeCounter(decrease);
                                console.log(decreaser()); // -1
                                console.log(decreaser()); // -2
                            
                        
                        함수 makeCounter는 보조 함수를 인자로 전달받고 함수를 반환하는 고차 함수이다. 
                            
                                function Counter() {
                                  // 카운트를 유지하기 위한 자유 변수
                                  var counter = 0;
                                  // 클로저
                                  this.increase = function () {
                                    return ++counter;
                                  };
                                  // 클로저
                                  this.decrease = function () {
                                    return --counter;
                                  };
                                }
                                const counter = new Counter();
                                console.log(counter.increase()); // 1
                                console.log(counter.decrease()); // 0
                            
                        
                        생성자 함수 Counter는 increase, decrease 메소드를 갖는 인스턴스를 생성한다. 
                            
                                var arr = [];
                                for (var i = 0; i < 5; i++) {
                                  arr[i] = function () {
                                    return i;
                                  };
                                }
                                for (var j = 0; j < arr.length; j++) {
                                  console.log(arr[j]());
                                }
                            
                        
                        배열 arr에 5개의 함수가 할당되고 각각의 함수는 순차적으로 0, 1, 2, 3, 4를 반환할 것으로 기대하겠지만 결과는 그렇지않다. 
                            
                                var arr = [];
                                for (var i = 0; i < 5; i++){
                                  arr[i] = (function (id) { // ②
                                    return function () {
                                      return id; // ③
                                    };
                                  }(i)); // ①
                                }
                                for (var j = 0; j < arr.length; j++) {
                                  console.log(arr[j]());
                                }
                            
                        
                        
                            
                                const arr = [];
                                for (let i = 0; i < 5; i++) {
                                  arr[i] = function () {
                                    return i;
                                  };
                                }
                                for (let i = 0; i < arr.length; i++) {
                                  console.log(arr[i]());
                                }
                            
                        
                        또는 함수형 프로그래밍 기법인 고차 함수를 사용하는 방법이 있다. 
                            
                                const arr = new Array(5).fill();
                                arr.forEach((v, i, array) => array[i] = () => i);
                                arr.forEach(f => console.log(f()));