Chapter 7: Using Closures - hochan222/Everything-in-JavaScript GitHub Wiki

See the Closure

ν΄λ‘œμ €λŠ” ν•¨μˆ˜μ—λ§Œ μžˆλ‹€. 객체와 ν΄λž˜μŠ€λŠ” ν΄λ‘œμ €λ₯Ό 갖을 수 μ—†μŠ΅λ‹ˆλ‹€.

Pointed Closure

=> ν™”μ‚΄ν‘œν•¨μˆ˜λŠ” λ²”μœ„λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

var student = students.find( 
  student =>
    // function scope: ORANGE(4)
    student.id == studentID
);

μ΄λ ‡κ²Œ => ν™”μ‚΄ν‘œ ν•¨μˆ˜ λ‚΄μ—μ„œλ„ studentID λ₯Ό μ°Έμ‘°ν•  수 μžˆμœΌλ―€λ‘œ ν΄λ‘œμ €κ°€ μœ μ§€ 될 수 μžˆμŒμ„ μ•Œμ•„μ•Όν•©λ‹ˆλ‹€.

Adding Up Closures

ν΄λ‘œμ €λŠ” 컴파일 νƒ€μž„μ— μ²˜λ¦¬λ˜λŠ” μ–΄νœ˜ λ²”μœ„λ₯Ό κΈ°λ°˜μœΌλ‘œν•˜μ§€λ§Œ ν΄λ‘œμ €λŠ” ν•¨μˆ˜ μΈμŠ€ν„΄μŠ€μ˜ λŸ°νƒ€μž„ νŠΉμ„±μœΌλ‘œ κ΄€μ°°λ©λ‹ˆλ‹€.

Live Link, Not a Snapshot

ν΄λ‘œμ €λŠ” μ‹€μ œλ‘œ 전체 λ³€μˆ˜ μžμ²΄μ— λŒ€ν•œ μ•‘μ„ΈμŠ€λ₯Ό μœ μ§€ν•˜λŠ” 라이브 λ§ν¬μž…λ‹ˆλ‹€. λ‹«νžŒ λ³€μˆ˜λ„ μ—…λ°μ΄νŠΈ(재 ν• λ‹Ή) ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

ν΄λ‘œμ €λŠ” λ³€μˆ˜κ°€ μ•„λ‹Œ 값을 지ν–₯ν•©λ‹ˆλ‹€.

var keeps = [];

for (var i = 0; i < 3; i++) { 
  keeps[i] = function keepI(){
    // closure over `i`
  return i; 
  };
}

keeps[0](); // 3 -- WHY!?
keeps[1](); // 3
keeps[2](); // 3

이 μ„Έκ°œμ˜ ν΄λ‘œμ €λŠ” 각각 λ‹€λ₯Έ ν•¨μˆ˜λ₯Ό κ°–μ§€λ§Œ 같은 λ³€μˆ˜λ₯Ό κ³΅μœ ν•©λ‹ˆλ‹€.
ν•¨μˆ˜λ§ˆλ‹€ λ‹€λ₯Έ λ³€μˆ˜λ₯Ό λ§Œλ“€λ©΄ μ˜ˆμƒ λ™μž‘μ΄ λ©λ‹ˆλ‹€.

var keeps = [];

for (var i = 0; i < 3; i++) {
  // new `j` created each iteration, which gets 
  // a copy of the value of `i` at this moment 
  let j = i;

  // the `i` here isn't being closed over, so 
  // it's fine to immediately use its current 
  // value in each loop iteration
  keeps[i] = function keepEachJ(){
    // close over `j`, not `i`!
    return j; 
  };
}

keeps[0](); // 0
keeps[1](); // 1
keeps[2](); // 2

What If I Can’t See It?


function lookupStudent(studentID) { 
  return function nobody(){
    var msg = "Nobody's here yet.";
    console.log(msg); 
  };
}

var student = lookupStudent(112);

student();
// Nobody's here yet.

GCλŠ” studentIDκ°€ 더이상 ν˜ΈμΆœλ˜μ§€ μ•ŠμœΌλ―€λ‘œ μ •λ¦¬ν•˜λ € ν• κ²ƒμž…λ‹ˆλ‹€.

function greetStudent(studentName) { 
  return function greeting(){
    console.log(
    `Hello, ${ studentName }!`
    ); 
  };
}

greetStudent("Kyle");

μ—¬κΈ°μ„œ λ°˜ν™˜ 된 ν•¨μˆ˜λŠ” λ²„λ €μ§‘λ‹ˆλ‹€. λ”°λΌμ„œ 기술적으둜 JS 엔진이 잠깐 λ™μ•ˆ ν΄λ‘œμ €λ₯Ό μƒμ„±ν•˜λ”λΌλ„ 이 ν”„λ‘œκ·Έλž¨μ—μ„œλŠ” μ˜λ―ΈμžˆλŠ” λ°©μ‹μœΌλ‘œ κ΄€μ°°λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

λ”°λΌμ„œ 이 μ ˆμ— μžˆλŠ” λͺ¨λ“  μ˜ˆμ œλŠ” ν΄λ‘œμ €κ°€ μ•„λ‹™λ‹ˆλ‹€.

Observable Definition

ν΄λ‘œμ €λŠ” ν•¨μˆ˜μ—μ„œ μ ‘κ·Όν•  수 μ—†λŠ” λ™μ•ˆμ— λ°”κΉ₯ λ²”μœ„μ—μ„œ λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

  • ν•¨μˆ˜μ— ν¬ν•¨λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.
  • λ°–μ˜ Scopeμ—μ„œ 적어도 ν•˜λ‚˜μ˜ λ³€μˆ˜κ°€ μ°Έμ‘°λ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.
  • λ²”μœ„ λ°– μ²΄μΈμ—μ„œ λ‹€λ₯Έ λΆ„κΈ°μ—μ„œ ν˜ΈμΆœλ˜μ–΄μ•Ό ν•©λ‹ˆλ‹€.

The Closure Lifecycle and Garbage Collection (GC)

ν΄λ‘œμ €λŠ” 본질적으둜 ν•¨μˆ˜ μΈμŠ€ν„΄μŠ€μ— μ—°κ²°λ˜μ–΄ 있기 λ•Œλ¬Έμ— λ³€μˆ˜μ— λŒ€ν•œ ν΄λ‘œμ €λŠ” ν•΄λ‹Ή ν•¨μˆ˜μ— λŒ€ν•œ μ°Έμ‘°κ°€ μžˆλŠ” ν•œ μ§€μ†λ©λ‹ˆλ‹€. μ΅œμ’… ν•¨μˆ˜ μ°Έμ‘°κ°€ μ‚­μ œλ˜λ©΄ ν•΄λ‹Ή λ³€μˆ˜μ— λŒ€ν•œ λ§ˆμ§€λ§‰ ν΄λ‘œμ €κ°€ 사라지고 λ³€μˆ˜ μžμ²΄κ°€ GCλ©λ‹ˆλ‹€.

ν΄λ‘œμ €λŠ” μ‹œκ°„μ΄ 지남에 따라 λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ΄ κΈ‰μ¦ν•˜λŠ” λ³€μˆ˜μ˜ GCλ₯Ό 예기치 μ•Šκ²Œ 방지 ν•  수 μžˆμŠ΅λ‹ˆλ‹€. κ·Έλ ‡κΈ° λ•Œλ¬Έμ— 더 이상 ν•„μš”ν•˜μ§€ μ•Šμ„ λ•Œ ν•¨μˆ˜ μ°Έμ‘° (및 그에 λ”°λ₯Έ ν΄λ‘œμ €)λ₯Ό λ²„λ¦¬λŠ” 것이 μ€‘μš”ν•©λ‹ˆλ‹€.

Per Variable or Per Scope?

function storeStudentInfo(id,name,grade) { 
  return function getInfo(whichValue){
    // warning:
    // using `eval(..)` is a bad idea! 
    var val = eval(whichValue);
    return val;
  }; 
}

var info = storeStudentInfo(73,"Suzy",87);

info("name");
// Suzy

info("grade");
// 87

κ°œλ…μ μœΌλ‘œ ν΄λ‘œμ €λŠ” λ²”μœ„κ°€ μ•„λ‹Œ λ³€μˆ˜ λ³„λ‘œ μ΄λ£¨μ–΄μ§‘λ‹ˆλ‹€. Ajax 콜백, 이벀트 ν•Έλ“€λŸ¬ 및 기타 λͺ¨λ“  ν˜•νƒœμ˜ ν•¨μˆ˜ ν΄λ‘œμ €λŠ” 일반적으둜 λͺ…μ‹œ 적으둜 μ°Έμ‘°ν•˜λŠ” ν•­λͺ© 만 λ‹«λŠ” κ²ƒμœΌλ‘œ κ°„μ£Όλ©λ‹ˆλ‹€.

λ§Žμ€ μ΅œμ‹  JS 엔진은 λͺ…μ‹œμ μœΌλ‘œ μ°Έμ‘°λ˜μ§€ μ•Šμ€ ν΄λ‘œμ € λ²”μœ„μ—μ„œ λͺ¨λ“  λ³€μˆ˜λ₯Ό μ œκ±°ν•˜λŠ” μ΅œμ ν™”λ₯Ό μ μš©ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ eval(..)μ—μ„œ λ³Ό 수 μžˆλ“―μ΄ μ΄λŸ¬ν•œ μ΅œμ ν™”λ₯Ό 적용 ν•  μˆ˜μ—†λŠ” 상황이 있으며 ν΄λ‘œμ € λ²”μœ„μ—λŠ” λͺ¨λ“  μ›λž˜ λ³€μˆ˜κ°€ 계속 ν¬ν•¨λ©λ‹ˆλ‹€.

λͺ‡ λ…„ μ „κΉŒμ§€ λ§Œν•΄λ„ λ§Žμ€ JS 엔진이이 μ΅œμ ν™”λ₯Ό μ μš©ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€. μ›Ή μ‚¬μ΄νŠΈκ°€ μ΄λŸ¬ν•œ λΈŒλΌμš°μ €, 특히 κ΅¬ν˜• λ˜λŠ” μ €κ°€ν˜• κΈ°κΈ°μ—μ„œ 계속 싀행될 수 μžˆμŠ΅λ‹ˆλ‹€. 즉, 이벀트 ν•Έλ“€λŸ¬μ™€ 같은 수λͺ…이 κΈ΄ ν΄λ‘œμ €κ°€ μš°λ¦¬κ°€ μƒκ°ν–ˆλ˜ 것보닀 훨씬 였래 λ©”λͺ¨λ¦¬λ₯Ό 보유 ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ‚¬μ–‘μ΄μ•„λ‹ˆλΌ 선택적 μ΅œμ ν™”λΌμ„œ μš°λ¦¬κ°€ λ¬΄λΆ„λ³„ν•˜κ²Œ μ‚¬μš©ν•˜λ©΄ μ•ˆλ©λ‹ˆλ‹€.

λ”°λΌμ„œ μš°λ¦¬λŠ” ν•„μš”μ— λ”°λΌμ„œλŠ” λͺ…μ‹œμ μœΌλ‘œ 이런 것듀을 GCλ‚˜ μ΅œμ ν™”μ— μ˜μ‘΄ν•˜μ§€ μ•Šκ³  μ½”λ”©ν•  ν•„μš”κ°€ μžˆλ‹€.


function manageStudentGrades(studentRecords) { 
  var grades = studentRecords.map(getGrade);
  
  // unset `studentRecords` to prevent unwanted 
  // memory retention in the closure 
  studentRecords = null;

  return addGrade;
  // ..
}

studentRecords = null; 둜써 ν΄λ‘œμ €μ—μ„œ μ—†μ• μ€€λ‹€.

μš”μ•½: ν΄λ‘œμ €λ₯Ό 톡해 λ‚΄λΆ€ λ³€μˆ˜λ₯Ό μ°Έμ‘°ν•˜λŠ” λ™μ•ˆμ—λŠ” λ‚΄λΆ€ λ³€μˆ˜κ°€ μ°¨μ§€ν•˜λŠ” λ©”λͺ¨λ¦¬λ₯Ό GCκ°€ νšŒμˆ˜ν•˜μ§€ μ•ŠλŠ”λ‹€. λ”°λΌμ„œ ν΄λ‘œμ € μ‚¬μš©μ΄ λλ‚˜λ©΄ μ°Έμ‘°λ₯Ό μ œκ±°ν•˜λŠ” 것이 μ’‹λ‹€.