# 반환 함수

function makeCounter() {                    LexicalEnvironment                  global Lexical Environment
    let count = 0;              ---         of makeCounter() call   
    return function() {             |                                           makeCounter: function
        return count++;             |          count: 0         --outer-->      counter: undefined         --outer-->   null
    }                               |
}                               ___
let counter = makeCounter();
1
2
3
4
5
6
7

makeCounter()를 호출할 때마다, (익명함수에 대한) 새로운 렉시컬 환경 객체가 만들어 진다.

  • 중첩 함수가 생성이 되고 실행은 되지 않은 상태

# JS 모든 함수는 클로저

  • 클로저란 내부함수가 외부함수의 지역변수에 접근 할 수 있고, 외부함수는 외부함수의 지역변수를 사용하는 내부함수가 소멸될 때까지 소멸되지 않는 특성을 의미한다

호출 장소와 상관없이 모든 함수는 함수가 생성된 곳렉시컬 환경을 기억한다.

  • 함수는 [[Environment]] 라 불리는 숨김 프로퍼티를 갖는다
    • 함수가 만들어진 곳의 렉시컬 환경에 대한 참조가 저장된다.
    • 생성될 때, 딱 한번 그 값이 세팅된다.
function makeCounter() {                    
    let count = 0;                        ---            
    return function() {  [[Environment]] --> |                                makeCounter: function
        return count++;                      |   count: 0     --outer-->      counter: undefined       --outer-->   null
    }                                        |
}                                         ---
let counter = makeCounter();
1
2
3
4
5
6
7

counter()(makeCounter() 의 반환함수인 익명함수) 를 호출할 때마다, 새로운 렉시컬 환경이 만들어진다.

  • 새로운 렉시컬 환경이지만, counter.[[Environment]]에 저장된 렉시컬 환경을 외부 렉시컬 환경으로서 참조

    • counter()의 렉시컬 환경은 매번 만들어 지지만, makeCounter() 렉시컬은 별개로 새로 만들어 지지 않는 상태이다.
    • counter()에서 외부 렉시컬 환경의 변수를 참조할 때에는 딱 한번 만들어지는 counter.[[Environment]] 를 참조한다.

    싱글톤 같은 개념인가?

  • 변수값 갱신은 변수가 저장된 렉시컬 환경에서 이뤄진다.

    • counter() 을 여러번 호출하면 count 변수가 2, 3 .. 으로 증가하는 이유

# 클로저

외부변수를 기억하고 이 외부 변수에 접근할 수 있는 함수

# 클로저는 언제 사용할까?

  1. 자바스크립트 라이브러리나 모듈에서 private 으로 나의 변수를 보호하고 싶을 때. 즉, 지역 변수 보호

    function hello(name) {
      var _name = name;
      return function() {
        console.log('Hello, ' + _name);
      };
    }
    
    var hello1 = hello('승민');
    var hello2 = hello('현섭');
    var hello3 = hello('유근');
    
    hello1(); // 'Hello, 승민'
    hello2(); // 'Hello, 현섭'
    hello3(); // 'Hello, 유근'
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    • 사용자의 접근을 제한
    • 변수의 조작을 불가능하게 하기 위함.

    모듈 에서 한번 더 다룰 예정이다. TODO

  2. static 변수를 이용하고 싶을 때

    • 다른 라이브러리들과 함께 사용되는 경우 서로간 충돌을 없앰
    • 전역변수를 사용했다가 다른 라이브러리 가져왔는데 그 라이브러리에서 덮어씌워버린다면 이유도 모르고 멀쩡하던 웹 페이지에서 에러가 발생하게 될 것이다.

# 가비지 컬렉션

일반적으로, 함수 호출이 끝나면 함수의 렉시컬 환경이 메모리에서 제거 된다.

# 메모리에 유지되는 경우 🔗

도달 가능한 상태일 때

  • 중첩함수에서 [[Environment]] 프로퍼티에 외부 함수 렉시컬 환경에 대한 정보가 저장되어. 도달 가능한 상태가 된다.
  • 해제 :g = null;

# 최적화 프로세스

자바스크립트 엔진는 외부 변수가 사용되지 않는다고 판단되면 메모리에서 제거한다.

  • V8, 디버깅시 클로저의 변수를 사용할 수 없는 부작용이 있다.

# code

함수의 콜백의 이름이 있는 함수를 쓴다.

function inBetween(a, b) {
   return function(x) {
     return x  a && x <= b;
   };
 }
let arr = [1, 2, 3, 4, 5, 6, 7];
alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6
1
2
3
4
5
6
7

필드를 기준으로 정렬하기

let users = [
 { name: "John", age: 20, surname: "Johnson" },
 { name: "Pete", age: 18, surname: "Peterson" },
 { name: "Ann", age: 19, surname: "Hathaway" }
];
function byField(field) {
 return (a, b) => a[field] > b[field] ? 1 : -1;
}

users.sort(byField('name'));
users.sort(byField('age'));
1
2
3
4
5
6
7
8
9
10
11