μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ μŠ€μ½”ν”„μ™€ ν΄λ‘œμ €


Overview

javascript-overview

기본적으둜 μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” ECMAScript μ–Έμ–΄ λͺ…μ„Έλ₯Ό λ”°λ₯΄κ³ μžˆλ‹€. 이 λͺ…μ„Έ 8μž₯의 μ‹€ν–‰μ½”λ“œμ™€ μ‹€ν–‰μ»¨ν…μŠ€νŠΈλΆ€λΆ„μ—μ„œ μŠ€μ½”ν”„μ— κ΄€ν•œ λ™μž‘ 방식을 확인할 수 있으며, 또 μ€‘μš”ν•œ κ°œλ…μΈ 1κΈ‰ κ°μ²΄λ‘œμ„œμ˜ ν•¨μˆ˜λŠ” κ·Έ νŠΉμ§•μ„ λͺ…μ„Έμ˜ μ „λ°˜μ μΈ λΆ€λΆ„μ—μ„œ λ‚˜νƒ€λ‚΄κ³  μžˆλ‹€. 그리고, ν΄λ‘œμ €(Closure)에 λŒ€ν•œ μ •μ˜λŠ” μ—†λ‹€. ν΄λ‘œμ €λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈκ°€ μ±„μš©ν•˜κ³  μžˆλŠ” 기술적 기반 ν˜Ήμ€ μ»¨μ…‰μœΌλ‘œ, μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” ν΄λ‘œμ €λ₯Ό μ΄μš©ν•˜μ—¬ μŠ€μ½”ν”„μ  νŠΉμ§•κ³Ό 일급 κ°μ²΄λ‘œμ„œμ˜ ν•¨μˆ˜μ— λŒ€ν•œ λͺ…μ„Έλ₯Ό κ΅¬ν˜„ν•œ 것이닀.

μŠ€μ½”ν”„

κΉ€μΆ˜μˆ˜ μ‹œμΈμ˜ "꽃"μ΄λΌλŠ” μ‹œλ₯Ό 보면, μ–΄λ–€ ν•˜λ‚˜μ˜ λͺΈμ§“은 이름을 톡해 의미λ₯Ό λΆ€μ—¬λ°›κ³  꽃이 λœλ‹€.

ν”„λ‘œκ·Έλž˜λ°λ„ λ§ˆμ°¬κ°€μ§€λ‘œ λ³€μˆ˜λ‚˜ ν•¨μˆ˜μ— 이름을 λΆ€μ—¬ν•˜μ—¬ 의미λ₯Ό 갖도둝 ν•œλ‹€. λ§Œμ•½ 이름이 μ—†λ‹€λ©΄, λ³€μˆ˜λ‚˜ ν•¨μˆ˜λŠ” λ‹€λ§Œ κ·Έμ € ν•˜λ‚˜μ˜ λ©”λͺ¨λ¦¬ μ£Όμ†Œμ— μ§€λ‚˜μ§€ μ•ŠλŠ”λ‹€. κ·Έλž˜μ„œ ν”„λ‘œκ·Έλž¨μ€ "이름:κ°’"의 λŒ€μ‘ν‘œλ₯Ό λ§Œλ“€μ–΄ μ‚¬μš©ν•œλ‹€. 이 λŒ€μ‘ν‘œμ˜ 이름을 가지고 μ½”λ“œλ₯Ό 보닀 μ‰½κ²Œ μ΄ν•΄ν•˜κ³ , 또 이름을 톡해 값을 μ €μž₯ν•˜κ³ , λ‹€μ‹œ 가져와 μˆ˜μ •ν•œλ‹€.

초기 ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄λŠ” 이 λŒ€μ‘ν‘œλ₯Ό ν”„λ‘œκ·Έλž¨ μ „μ²΄μ—μ„œ ν•˜λ‚˜λ‘œ κ΄€λ¦¬ν–ˆλŠ”λ°, μ—¬κΈ°μ—λŠ” 이름 좩돌의 λ¬Έμ œκ°€ μžˆμ—ˆλ‹€. κ·Έλž˜μ„œ μΆ©λŒμ„ ν”Όν•˜κΈ° μœ„ν•΄, 각 μ–Έμ–΄λ§ˆλ‹€ "μŠ€μ½”ν”„"λΌλŠ” κ·œμΉ™μ„ λ§Œλ“€μ–΄ μ •μ˜ν•˜μ˜€λ‹€. κ·Έλ ‡κ²Œ μŠ€μ½”ν”„ κ·œμΉ™μ€ μ–Έμ–΄μ˜ λͺ…μ„Έ(Specification)κ°€ λ˜μ—ˆλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈλ„ λ§ˆμ°¬κ°€μ§€λ‘œ μžμ‹ μ˜ μŠ€μ½”ν”„ κ·œμΉ™μ΄ μžˆλ‹€.
μžλ°”μŠ€ν¬λ¦½νŠΈ(ES6)λŠ” ν•¨μˆ˜ 레벨과 블둝 레벨의 λ ‰μ‹œμ»¬ μŠ€μ½”ν”„κ·œμΉ™μ„ λ”°λ₯Έλ‹€.

μŠ€μ½”ν”„ 레벨

μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” μ „ν†΅μ μœΌλ‘œ ν•¨μˆ˜ 레벨 μŠ€μ½”ν”„λ₯Ό 지원해왔고, μ–Όλ§ˆμ „κΉŒμ§€λ§Œ 해도 블둝 레벨 μŠ€μ½”ν”„λŠ” μ§€μ›ν•˜μ§€ μ•Šμ•˜λ‹€. ν•˜μ§€λ§Œ κ°€μž₯ μ΅œμ‹  λͺ…세인 ES6(ECMAScript 6)λΆ€ν„° 블둝 레벨 μŠ€μ½”ν”„λ₯Ό μ§€μ›ν•˜κΈ° μ‹œμž‘ν–ˆλ‹€.

ν•¨μˆ˜ 레벨 μŠ€μ½”ν”„

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ varν‚€μ›Œλ“œλ‘œ μ„ μ–Έλœ λ³€μˆ˜λ‚˜, ν•¨μˆ˜ μ„ μ–Έμ‹μœΌλ‘œ λ§Œλ“€μ–΄μ§„ ν•¨μˆ˜λŠ” ν•¨μˆ˜ 레벨 μŠ€μ½”ν”„λ₯Ό κ°–λŠ”λ‹€. 즉, ν•¨μˆ˜ λ‚΄λΆ€ μ „μ²΄μ—μ„œ μœ νš¨ν•œ μ‹λ³„μžκ°€ λœλ‹€.

μ•„λž˜ μ½”λ“œλŠ” μ•„λ¬΄λŸ° λ¬Έμ œμ—†μ΄ blueλ₯Ό 좜λ ₯ν•œλ‹€.

function foo() {
  if (true) {
    var color = "blue";
  }
  console.log(color); // blue
}
foo();

λ§Œμ•½ var colorκ°€ 블둝 레벨 μŠ€μ½”ν”„μ˜€λ‹€λ©΄, colorλŠ” if문이 λλ‚ λ•Œ 파괴되고 console.logμ—μ„œ 잘λͺ»λœ 참쑰둜 μ—λŸ¬κ°€ λ°œμƒν•  것이닀. κ·Έλ ‡μ§€λ§Œ colorλŠ” ν•¨μˆ˜ 레벨의 μŠ€μ½”ν”„μ΄κΈ° λ•Œλ¬Έμ— foo ν•¨μˆ˜ λ‚΄λΆ€ μ–΄λ””μ—μ„œλ“  μ—λŸ¬ λ°œμƒ 없이 μ°Έμ‘°ν•  수 μžˆλ‹€.

블둝 레벨 μŠ€μ½”ν”„

ES6의 let, constν‚€μ›Œλ“œλŠ” 블둝 레벨 μŠ€μ½”ν”„ λ³€μˆ˜λ₯Ό λ§Œλ“€μ–΄ μ€€λ‹€.

function foo() {
  if (true) {
    let color = "blue";
    console.log(color); // blue
  }
  console.log(color); // ReferenceError: color is not defined
}
foo();

let colorλ₯Ό if블둝 λ‚΄λΆ€μ—μ„œ μ„ μ–Έν•˜μ˜€λ‹€. λ•Œλ¬Έμ— if블둝 λ‚΄λΆ€μ—μ„œ μ°Έμ‘°ν•  수 있으며, κ·Έ λ°–μ˜ μ˜μ—­μ—μ„œ 잘λͺ»λœ 참쑰둜 μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

var vs let, const

ES6κ°€ ν‘œμ€€ν™”λ˜λ©΄μ„œ, 블둝 레벨과 ν•¨μˆ˜ λ ˆλ²¨μ„ λͺ¨λ‘ μ§€μ›ν•˜κ²Œ λ˜μ—ˆλ‹€. "You don't know JS" μ‹œλ¦¬μ¦ˆμ˜ μ €μžμΈ Kyle Simpson은 var, let, constκ°€ μ„œλ‘œ λ‹€λ₯΄κΈ°μ— ν•„μš”ν•œ 상황에 μ•Œλ§žκ²Œ μ‚¬μš©ν•  쀄 μ•Œμ•„μ•Ό ν•œλ‹€κ³  μ„€λͺ…ν•˜κ³  μžˆλ‹€.

κ·Έλ ‡μ§€λ§Œ μš”μ¦ˆμŒ ES6 μ½”λ“œ λŒ€λΆ€λΆ„μ€ varλ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”λ‹€. varλŠ” letκ³Ό const둜 λͺ¨λ‘ λŒ€μ²΄κ°€ κ°€λŠ₯ν•˜κ³ , varμžμ²΄κ°€ ν•¨μˆ˜ 레벨의 μŠ€μ½”ν”„λ₯Ό 가지기 λ•Œλ¬Έμ— 블둝 레벨 μŠ€μ½”ν”„λ³΄λ‹€ 더 λ§Žμ€ ν˜Όλž€μ„ μ•ΌκΈ°ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

λ ‰μ‹œμ»¬ μŠ€μ½”ν”„

λ ‰μ‹œμ»¬ μŠ€μ½”ν”„(Lexical scope)λŠ” 보톡 동적 μŠ€μ½”ν”„(Dynamic scope)와 많이 λΉ„κ΅ν•œλ‹€.

μœ„ν‚€ν”Όλ””μ•„λ₯Ό 보면 동적 μŠ€μ½”ν”„μ™€ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λ₯Ό λ‹€μŒκ³Ό 같이 μ •μ˜ν•˜κ³  μžˆλ‹€.

  • 동적 μŠ€μ½”ν”„

    The name resolution depends upon the program state when the name is encountered which is determined by the execution context or calling context.

  • λ ‰μ‹œμ»¬ μŠ€μ½”ν”„ (정적 μŠ€μ½”ν”„(Static scope) λ˜λŠ” μˆ˜μ‚¬μ  μŠ€μ½”ν”„(Rhetorical scope))

    The name resolution depends on the location in the source code and the lexical context, which is defined by where the named variable or function is defined.

동적 μŠ€μ½”ν”„λŠ” ν”„λ‘œκ·Έλž¨μ˜ λŸ°νƒ€μž„ λ„μ€‘μ˜ μ‹€ν–‰ μ»¨ν…μŠ€νŠΈλ‚˜ 호좜 μ»¨ν…μŠ€νŠΈμ— μ˜ν•΄ κ²°μ •λ˜κ³ , λ ‰μ‹œμ»¬ μŠ€μ½”ν”„μ—μ„œλŠ” μ†ŒμŠ€μ½”λ“œκ°€ μž‘μ„±λœ κ·Έ λ¬Έλ§₯μ—μ„œ κ²°μ •λœλ‹€. ν˜„λŒ€ ν”„λ‘œκ·Έλž˜λ°μ—μ„œ λŒ€λΆ€λΆ„μ˜ 언어듀은 λ ‰μ‹œμ»¬ μŠ€μ½”ν”„ κ·œμΉ™μ„ λ”°λ₯΄κ³  μžˆλ‹€.

동적 μŠ€μ½”ν”„μ™€ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ™€ Perl을 λΉ„κ΅ν•˜μ—¬ 확인할 수 μžˆλ‹€. μ•„λž˜λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ™€ Perl둜 같은 μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ˜€μ„ λ•Œ λ‚˜μ˜€λŠ” 결과이닀. lexical-scope-js

μžλ°”μŠ€ν¬λ¦½νŠΈλŠ”λŠ” λ ‰μ‹œμ»¬ μŠ€μ½”ν”„ κ·œμΉ™μ„ 톡해 global, global을 좜λ ₯ν•˜μ˜€μœΌλ©°, Perl은 동적 μŠ€μ½”ν”„ κ·œμΉ™μ„ 톡해 local, global을 좜λ ₯ν•˜μ˜€λ‹€. (참고둜 Perlμ—μ„œ localλŒ€μ‹  myν‚€μ›Œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ λ³€μˆ˜μ˜ μœ νš¨λ²”μœ„λ₯Ό μ œν•œν•˜μ—¬, μžλ°”μŠ€ν¬λ¦½νŠΈμ™€ 같은 κ²°κ³Όλ₯Ό 얻을 수 μžˆλ‹€.)

λ ‰μ‹œμ»¬ μŠ€μ½”ν”„ κ·œμΉ™μ„ λ”°λ₯΄λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ ν•¨μˆ˜λŠ” 호좜 μŠ€νƒκ³Ό 관계없이 각각의 (thisλ₯Ό μ œμ™Έν•œ)λŒ€μ‘ν‘œλ₯Ό μ†ŒμŠ€μ½”λ“œ κΈ°μ€€μœΌλ‘œ μ •μ˜ν•˜κ³ , λŸ°νƒ€μž„μ— κ·Έ λŒ€μ‘ν‘œλ₯Ό λ³€κ²½μ‹œν‚€μ§€ μ•ŠλŠ”λ‹€. (사싀 λŸ°νƒ€μž„μ— λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λ₯Ό μˆ˜μ •ν•  수 μžˆλŠ” 방법듀(eval, with)이 μžˆμ§€λ§Œ, ꢌμž₯ν•˜μ§€ μ•ŠλŠ”λ‹€.)

쀑첩 μŠ€μ½”ν”„(μŠ€μ½”ν”„ 체인 λ˜λŠ” μŠ€μ½”ν”„ 버블)

μš°λ¦¬κ°€ λ§ν•˜λŠ” 이 μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ μŠ€μ½”ν”„λŠ” ECMAScript μ–Έμ–΄ λͺ…μ„Έμ—μ„œ λ ‰μ‹œμ»¬ ν™˜κ²½(Lexical environment)κ³Ό ν™˜κ²½ λ ˆμ½”λ“œ(Environment Record)λΌλŠ” κ°œλ…μœΌλ‘œ μ •μ˜λ˜μ—ˆλ‹€.

6.2.5 The Lexical Environment and Environment Record Specification Types

The Lexical Environment and Environment Record types are used to explain the behaviour of name resolution in nested functions and blocks. These types and the operations upon them are defined in 8.1.

κ°„λ‹¨ν•˜κ²Œ 그림으둜 ν‘œν˜„ν•΄λ³΄λ©΄ μ•„λž˜μ™€ 같은 ν˜•νƒœλ‘œ λ³Ό 수 μžˆλ‹€. execution-context

μ•žμ—μ„œ μ„€λͺ…ν•œ "이름:κ°’μ˜ λŒ€μ‘ν‘œ"κ°€ ν™˜κ²½ λ ˆμ½”λ“œμ™€ κ°™λ‹€κ³  λ³Ό 수 있고, λ ‰μ‹œμ»¬ ν™˜κ²½μ€ 이 ν™˜κ²½ λ ˆμ½”λ“œμ™€ μƒμœ„ λ ‰μ‹œμΌ€ ν™˜κ²½(Outer lexical environment)에 λŒ€ν•œ 참쑰둜 이루어진닀.

ν˜„μž¬-λ ‰μ‹œμ»¬ ν™˜κ²½μ˜ λŒ€μ‘ν‘œ(ν™˜κ²½ λ ˆμ½”λ“œ)μ—μ„œ λ³€μˆ˜λ₯Ό 찾아보고, μ—†λ‹€λ©΄ λ°”κΉ₯ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ μ°Έμ‘°ν•˜μ—¬ μ°Ύμ•„λ³΄λŠ” μ‹μœΌλ‘œ 쀑첩 μŠ€μ½”ν”„κ°€ κ°€λŠ₯해진닀. 이 쀑첩 μŠ€μ½”ν”„ 탐색은 ν•΄λ‹Ήν•˜λŠ” 이름을 μ°Ύλ˜κ°€ μ•„λ‹ˆλ©΄ λ°”κΉ₯ λ ‰μ‹œμ»¬ ν™˜κ²½ μ°Έμ‘°κ°€ null이 λ λ•Œ 탐색을 λ©ˆμΆ˜λ‹€.

scope-chain

μ°Έκ³ : ECMA-262 Edition3λ₯Ό 보면 μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ μŠ€μ½”ν”„μ  νŠΉμ§•μ€ Scope chain(=list)κ³Ό Activation Objectλ“±μ˜ κ°œλ…μœΌλ‘œ μ„€λͺ…ν•˜μ˜€λ‹€. 그리고 이 μ„€λͺ…듀이 μ „λ°˜μ μœΌλ‘œ 널리 μ•Œλ €μ‘Œμ§€λ§Œ, 이 λ‹€μŒ λͺ…세인 ECMA262 Edition5λΆ€ν„°λŠ” Lexical Environment와 Environment Record의 κ°œλ…μœΌλ‘œ μŠ€μ½”ν”„λ₯Ό μ„€λͺ…ν•˜κ³  μžˆλ‹€.

ν˜Έμ΄μŠ€νŒ…

전톡적인 μžλ°”μŠ€ν¬λ¦½νŠΈ μŠ€μ½”ν”„μ˜ νŠΉμ§•μ€ λ‹€μŒ 두 κ°€μ§€λΌλŠ” 것을 μ•Œμ•˜λ‹€.

  • λ ‰μ‹œμ»¬ μŠ€μ½”ν”„
  • ν•¨μˆ˜ 레벨 μŠ€μ½”ν”„ (+ 블둝 레벨 μŠ€μ½”ν”„-ES6)

그럼 μ•„λž˜μ™€ 같은 상황에선 μ–΄λ–€ 값이 좜λ ₯될까?

function foo() {
  a = 2;
  var a;

  console.log(a);
}
foo();

2κ°€ 정상적(?)으둜 좜λ ₯λœλ‹€.
그럼 λ‹€μŒμ€ 어떨지 μƒκ°ν•΄λ³΄μž.

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

μ΄λ²ˆμ—λŠ” undefinedκ°€ 좜λ ₯λœλ‹€. 쑰금 어이없닀고 λŠλ‚„ 수 μžˆμ§€λ§Œ, 사싀 μ•Œκ³ λ³΄λ©΄ μ–΄μ΄μ—†λŠ” 일은 μ•„λ‹ˆλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈ 엔진은 μ½”λ“œλ₯Ό μΈν„°ν”„λ¦¬νŒ… ν•˜κΈ°μ „μ— κ·Έ μ½”λ“œλ₯Ό λ¨Όμ € μ»΄νŒŒμΌν•œλ‹€. var a = 2;λ₯Ό ν•˜λ‚˜μ˜ ꡬ문으둜 생각할 μˆ˜λ„ μžˆμ§€λ§Œ, μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” λ‹€μŒ 두 개의 ꡬ문으둜 λΆ„λ¦¬ν•˜μ—¬ λ³Έλ‹€.

  1. var a;
  2. a = 2;

λ³€μˆ˜ μ„ μ–Έ(생성) 단계와 μ΄ˆκΈ°ν™” 단계λ₯Ό λ‚˜λˆ„κ³ , μ„ μ–Έ λ‹¨κ³„μ—μ„œλŠ” κ·Έ 선언이 μ†ŒμŠ€μ½”λ“œμ˜ 어디에 μœ„μΉ˜ν•˜λ“  ν•΄λ‹Ή μŠ€μ½”ν”„μ˜ μ»΄νŒŒμΌλ‹¨κ³„μ—μ„œ μ²˜λ¦¬ν•΄λ²„λ¦¬λŠ” 것이닀. (μ–Έμ–΄ μŠ€νŽ™μƒμœΌλ‘œ λ³€μˆ˜λŠ” λ ‰μ‹œμ»¬ ν™˜κ²½μ΄ μΈμŠ€ν„΄μŠ€ν™”λ˜κ³  μ΄ˆκΈ°ν™”λ λ•Œ μƒμ„±λœλ‹€κ³  ν•œλ‹€.) λ•Œλ¬Έμ— 이런 선언단계가 μŠ€μ½”ν”„μ˜ κΌ­λŒ€κΈ°λ‘œ ν˜Έμ΄μŠ€νŒ…("λŒμ–΄μ˜¬λ¦Ό")λ˜λŠ” μž‘μ—…μ΄λΌκ³  λ³Ό 수 μžˆλŠ”κ²ƒμ΄λ‹€.

μ°Έκ³ : λΈ”λ‘μŠ€μ½”ν”„μΈ let도 ν˜Έμ΄μŠ€νŒ…μ΄ λœλ‹€. κ·Έλ ‡μ§€λ§Œ 선언전에 μ°Έμ‘°ν•  경우 undefinedλ₯Ό λ°˜ν™˜ν•˜μ§€ μ•Šκ³  ReferenceErrorλ₯Ό λ°œμƒμ‹œν‚€λŠ” νŠΉμ§•μ΄ μžˆλ‹€.

Temporal dead zone and errors with let

In ECMAScript 2015, let will hoist the variable to the top of the block. However, referencing the variable in the block before the variable declaration results in a ReferenceError. The variable is in a "temporal dead zone" from the start of the block until the declaration is processed.

ν΄λ‘œμ €(Closure)

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ (μ–Έμ–΄ λͺ…세에 μ—†λŠ”) ν΄λ‘œμ €μ— λŒ€ν•œ μ •μ˜λŠ” κ½€ λ§Žμ€ μ‚¬λžŒλ“€μ΄ κ°€μž₯ κΆκΈˆν•΄ν•˜λŠ” 뢀뢄이닀.

μ•„λž˜λŠ” μ‹€μ œ v8μ—”μ§„μ˜ ν΄λ‘œμ € ν…ŒμŠ€νŠΈμ½”λ“œμ™€ μ‚¬λžŒλ“€μ΄ λ§ν•˜λŠ” ν΄λ‘œμ €μ˜ μ˜λ―Έλ“€μ΄λ‹€.

closure-overview

쒅합해보면 ν•¨μˆ˜κ°€ 무언가λ₯Ό κΈ°μ–΅ν•˜κ³  그것을 λ‹€μ‹œ μ‚¬μš©ν•œλ‹€λŠ” 것을 μ•Œ 수 μžˆμ§€λ§Œ, μ—¬μ „νžˆ λͺ¨ν˜Έν•˜κ²Œ λŠκ»΄μ§„λ‹€. 이 λͺ¨ν˜Έν•¨μ„ μ—†μ• κΈ° μœ„ν•΄ ν΄λ‘œμ €μ˜ 탄생뢀터 μ•Œμ•„λ΄μ•Ό ν•  것 κ°™λ‹€.

ν΄λ‘œμ €κ°€ κ°€μž₯ 처음 λ“±μž₯ν•œ 1964λ…„, Peter J. Landin의 λ…Όλ¬Έ(The Mechanical Evaluation of Expressions)을 보면 ν΄λ‘œμ €λ₯Ό λ‹€μŒκ³Ό 같이 μ •μ˜ν•˜κ³  μžˆλ‹€.

closure

μœ„ μ •μ˜λ₯Ό ν† λŒ€λ‘œ, ν΄λ‘œμ €λ₯Ό ν˜„λŒ€ ν”„λ‘œκ·Έλž˜λ°μ—μ„œ λ‹€μŒκ³Ό 같이 ν•΄μ„ν•˜μ—¬ μ •μ˜ν•  수 μžˆμ„κ²ƒ κ°™λ‹€.

   ν΄λ‘œμ € =
     ν•¨μˆ˜ + ν•¨μˆ˜λ₯Ό λ‘˜λŸ¬μ‹Ό ν™˜κ²½(Lexical environment)

ν•¨μˆ˜λ₯Ό λ‘˜λŸ¬μ‹Ό ν™˜κ²½μ΄λΌλŠ”κ²ƒμ΄ λ°”λ‘œ μ•žμ—μ„œ μ„€λͺ…ν–ˆλ˜ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„μ΄λ‹€. ν•¨μˆ˜λ₯Ό λ§Œλ“€κ³  κ·Έ ν•¨μˆ˜ λ‚΄λΆ€μ˜ μ½”λ“œκ°€ νƒμƒ‰ν•˜λŠ” μŠ€μ½”ν”„λ₯Ό ν•¨μˆ˜ 생성 λ‹Ήμ‹œμ˜ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„λ‘œ κ³ μ •ν•˜λ©΄ λ°”λ‘œ ν΄λ‘œμ €κ°€ λ˜λŠ”κ²ƒμ΄λ‹€.

이제 이 ν΄λ‘œμ €κ°€ μžλ°”μŠ€ν¬λ¦½νŠΈμ— μ–΄λ–»κ²Œ λ…Ήμ•„λ“€μ–΄κ°”λŠ”μ§€ μ‚΄νŽ΄λ³΄λ„λ‘ ν•˜μž.

μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ ν΄λ‘œμ €

  • μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ ν΄λ‘œμ €λŠ” ν•¨μˆ˜κ°€ μƒμ„±λ˜λŠ” μ‹œμ μ— μƒμ„±λœλ‹€.
    = ν•¨μˆ˜κ°€ 생성될 λ•Œ κ·Έ ν•¨μˆ˜μ˜ λ ‰μ‹œμ»¬ ν™˜κ²½μ„ 포섭(closure)ν•˜μ—¬ 싀행될 λ•Œ μ΄μš©ν•œλ‹€.

λ”°λΌμ„œ κ°œλ…μ μœΌλ‘œ μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ λͺ¨λ“  ν•¨μˆ˜λŠ” ν΄λ‘œμ €μ΄μ§€λ§Œ, μ‹€μ œλ‘œ μš°λ¦¬λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ λͺ¨λ“  ν•¨μˆ˜λ₯Ό μ „λΆ€ ν΄λ‘œμ €λΌκ³  λΆ€λ₯΄μ§€λŠ” μ•ŠλŠ”λ‹€.

λ‹€μŒ μ˜ˆμ‹œλ“€μ„ ν†΅ν•΄μ„œ ν΄λ‘œμ €λ₯Ό 쑰금 더 μ •ν™•νžˆ νŒŒμ•…ν•  수 μžˆλ‹€.

function foo() {
  var color = "blue";
  function bar() {
    console.log(color);
  }
  bar();
}

foo();

barν•¨μˆ˜λŠ” μš°λ¦¬κ°€ λΆ€λ₯΄λŠ” ν΄λ‘œμ €μΌκΉŒ μ•„λ‹κΉŒ?

일단 barλŠ” fooμ•ˆμ— μ†ν•˜κΈ° λ•Œλ¬Έμ— fooμŠ€μ½”ν”„λ₯Ό μ™ΈλΆ€ μŠ€μ½”ν”„(outer lexical environment) 참쑰둜 μ €μž₯ν•œλ‹€. 그리고 barλŠ” μžμ‹ μ˜ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„ 체인을 톡해 foo의 colorλ₯Ό μ •ν™•νžˆ μ°Έμ‘°ν•  것이닀.

그럼 ν΄λ‘œμ €λΌ λ³Ό 수 μžˆμ§€ μ•Šμ„κΉŒ?

μ•„λ‹ˆλ‹€. μš°λ¦¬κ°€ λΆ€λ₯΄λŠ” ν΄λ‘œμ €λΌκ³  ν•˜κΈ°μ—λŠ” μ•½κ°„ 거리가 μžˆλ‹€. barλŠ” fooμ•ˆμ—μ„œ μ •μ˜λ˜κ³  μ‹€ν–‰λ˜μ—ˆμ„ 뿐, fooλ°–μœΌλ‘œ λ‚˜μ˜€μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ— ν΄λ‘œμ €λΌκ³  λΆ€λ₯΄μ§€ μ•ŠλŠ”λ‹€.

λŒ€μ‹ , λ‹€μŒ μ½”λ“œλŠ” μš°λ¦¬κ°€ μ‹€μ œλ‘œ λΆ€λ₯΄λŠ” ν΄λ‘œμ €λ₯Ό λ‚˜νƒ€λ‚΄κ³  μžˆλ‹€.

var color = "red";

function foo() {
  var color = "blue"; // 2
  function bar() {
    console.log(color); // 1
  }
  return bar;
}

var baz = foo(); // 3
baz(); // 4
  1. barλŠ” colorλ₯Ό μ°Ύμ•„ 좜λ ₯ν•˜λŠ” ν•¨μˆ˜λ‘œ μ •μ˜λ˜μ—ˆλ‹€.
  2. 그리고 barλŠ” outer environment 참쑰둜 foo의 environmentλ₯Ό μ €μž₯ν•˜μ˜€λ‹€.
  3. barλ₯Ό global의 bazλž€ μ΄λ¦„μœΌλ‘œ 데렀왔닀.
  4. globalμ—μ„œ baz(=bar)λ₯Ό ν˜ΈμΆœν–ˆλ‹€.
  5. barλŠ” μžμ‹ μ˜ μŠ€μ½”ν”„μ—μ„œ colorλ₯Ό μ°ΎλŠ”λ‹€.
  6. μ—†λ‹€. μžμ‹ μ˜ outer environment μ°Έμ‘°λ₯Ό μ°Ύμ•„κ°„λ‹€.
  7. outer environment인 foo의 μŠ€μ½”ν”„λ₯Ό 뒀진닀. colorλ₯Ό μ°Ύμ•˜λ‹€. 값은 blue이닀.
  8. λ•Œλ¬Έμ— λ‹Ήμ—°νžˆ blueκ°€ 좜λ ₯λœλ‹€.

이게 λ°”λ‘œ ν΄λ‘œμ €λ‹€. κ·Έλƒ₯ λ‹¨μˆœν•˜κ²Œ 보면 "이 λ‹Ήμ—°ν•˜κ²Œ μ™œ?"라고 생각할 수 μžˆμ§€λ§Œ, 쑰금 더 μžμ„Ένžˆ 따져보도둝 ν•˜μž.

일단 μ€‘μš”ν•œ 뢀뢄은 2~4번, 그리고 7λ²ˆμ΄λ‹€. barλŠ” μžμ‹ μ΄ μƒμ„±λœ λ ‰μ‹œμ»¬ μŠ€μ½”ν”„μ—μ„œ λ²—μ–΄λ‚˜ globalμ—μ„œ bazλΌλŠ” μ΄λ¦„μœΌλ‘œ 호좜이 λ˜μ—ˆκ³ , μŠ€μ½”ν”„ 탐색은 ν˜„μž¬ μ‹€ν–‰ μŠ€νƒκ³Ό κ΄€λ ¨ μ—†λŠ” fooλ₯Ό 거쳐 κ°”λ‹€. bazλ₯Ό bar둜 μ΄ˆκΈ°ν™” ν•  λ•ŒλŠ” 이미 bar의 outer lexical environmentλ₯Ό foo둜 κ²°μ •ν•œ 이후이닀. λ•Œλ¬Έμ—, bar의 생성과 직접적인 관련이 μ—†λŠ” globalμ—μ„œ 아무리 ν˜ΈμΆœν•˜λ”λΌλ„ μ—¬μ „νžˆ fooμ—μ„œ colorλ₯Ό μ°ΎλŠ” 것이닀. 이런 bar(λ˜λŠ” baz)와 같은 ν•¨μˆ˜λ₯Ό μš°λ¦¬λŠ” ν΄λ‘œμ €λΌκ³  λΆ€λ₯Έλ‹€.

closure

μ—¬κΈ°μ—μ„œ λ‹€μ‹œν•œλ²ˆ κ°•μ‘°ν•˜μ§€λ§Œ JS의 μŠ€μ½”ν”„λŠ” λ ‰μ‹œμ»¬ μŠ€μ½”ν”„, 즉 μ΄λ¦„μ˜ λ²”μœ„λŠ” μ†ŒμŠ€μ½”λ“œκ°€ μž‘μ„±λœ κ·Έ λ¬Έλ§₯μ—μ„œ λ°”λ‘œ κ²°μ •λ˜λŠ” 것이닀.

μΆ”κ°€λ‘œ, foo의 λ ‰μ‹œμ»¬ν™˜κ²½ μΈμŠ€ν„΄μŠ€λŠ” foo();μˆ˜ν–‰μ΄ λλ‚œ 이후 GCκ°€ νšŒμˆ˜ν•΄μ•Ό ν•˜λŠ”λ° 사싀을 그렇지 μ•Šλ‹€. μ•žμ— μ„€λͺ…ν–ˆλ“― barλŠ” μ—¬μ „νžˆ λ°”κΉ₯ λ ‰μ‹œμ»¬ ν™˜κ²½μΈ foo의 λ ‰μ‹œμ»¬ ν™˜κ²½μ„ 계속 μ°Έμ‘°ν•˜κ³ μžˆκ³ , 이 barλŠ” bazκ°€ μ—¬μ „νžˆ μ°Έμ‘°ν•˜κ³  있기 λ•Œλ¬Έμ΄λ‹€.(baz(=bar) -> foo)

gc-closure

유λͺ…ν•˜κ³  또 유λͺ…ν•œ 반볡문 ν΄λ‘œμ €

function count() {
  var i;
  for (i = 1; i < 10; i += 1) {
    setTimeout(function timer() {
      console.log(i);
    }, i * 100);
  }
}
count();

이 μ½”λ“œλŠ” 1, 2, 3, ... 9λ₯Ό 0.1μ΄ˆλ§ˆλ‹€ 좜λ ₯ν•˜λŠ” 것이 λͺ©ν‘œμ˜€λŠ”데, κ²°κ³Όλ‘œλŠ” 10이 9번 좜λ ₯λ˜μ—ˆλ‹€. μ™œμΌκΉŒ?

timerλŠ” ν΄λ‘œμ €λ‘œ μ–Έμ œ μ–΄λ””μ„œ μ–΄λ–»κ²Œ ν˜ΈμΆœλ˜λ˜μ§€ 항상 μƒμœ„ μŠ€μ½”ν”„μΈ countμ—κ²Œ iλ₯Ό μ•Œλ €λ‹¬λΌκ³  μš”μ²­ν•  것이닀. 그리고 timerλŠ” 0.1초 ν›„ ν˜ΈμΆœλœλ‹€. 그런데 첫 0.1μ΄ˆκ°€ 지날 λ™μ•ˆ 이미 iλŠ” 10이 λ˜μ—ˆλ‹€. 그리고 timerλŠ” 0.1초 주기둜 호좜될 λ•Œλ§ˆλ‹€ 항상 countμ—μ„œ iλ₯Ό μ°ΎλŠ”λ‹€. κ²°κ΅­, timerλŠ” 이미 10이 λ˜μ–΄λ²„λ¦° i만 좜λ ₯ν•˜κ²Œ λœλ‹€.

그럼 μ˜λ„λŒ€λ‘œ 1~9κΉŒμ§€ μ°¨λ‘€λŒ€λ‘œ 좜λ ₯ν•˜κ³  μ‹ΆμœΌλ©΄ μ–΄λ–»κ²Œ ν•΄μ•Όν• κΉŒ?

  1. μƒˆλ‘œμš΄ μŠ€μ½”ν”„λ₯Ό μΆ”κ°€ν•˜μ—¬ λ°˜λ³΅μ‹œλ§ˆλ‹€ 그곳에 각각 λ”°λ‘œ 값을 μ €μž₯ν•˜λŠ” 방식
  2. ES6μ—μ„œ μΆ”κ°€λœ 블둝 μŠ€μ½”ν”„λ₯Ό μ΄μš©ν•˜λŠ” 방식

μ΄λ ‡κ²Œ 두 가지가 μžˆμ„ 것이닀.

λ‹€μŒ μ½”λ“œλŠ” μ›λž˜ μ˜λ„λŒ€λ‘œ λ™μž‘ν•œλ‹€.

1.

function count() {
  var i;
  for (i = 1; i < 10; i += 1) {
    (function(countingNumber) {
      setTimeout(function timer() {
        console.log(countingNumber);
      }, i * 100);
    })(i);
  }
}
count();

2.

function count() {
  "use strict";
  for (let i = 1; i < 10; i += 1) {
    setTimeout(function timer() {
      console.log(i);
    }, i * 100);
  }
}
count();

Reference

이민규2016.03.11
Back to list