avatar
Published on

Javascript - Closure

Author
  • avatar
    Name
    yceffort

ν΄λ‘œμ €

μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” μ–΄λ–»κ²Œ λ³€μˆ˜μ˜ 유효 λ²”μœ„λ₯Ό μ •ν•˜λŠ”κ°€?

function hello() {
  var name = 'yceffort'
  // λ‚΄λΆ€ν•¨μˆ˜μ΄λ©°, ν΄λ‘œμ €λ‹€.
  function showName() {
    // λΆ€λͺ¨ν•¨μˆ˜κ°€ μ„ μ–Έν•œ λ³€μˆ˜λ₯Ό μ‚¬μš©ν•œλ‹€.
    alert(`hello, ${name}`)
  }
  showName()
}
hello()

μ—¬κΈ°μ—μ„œ hello()λŠ” μ§€μ—­λ³€μˆ˜ nameκ³Ό ν•¨μˆ˜ showName()을 μƒμ„±ν–ˆλ‹€. showName()은 λ‚΄λΆ€ν•¨μˆ˜μ΄λ―€λ‘œ, hello()μ—μ„œλ§Œ μ‚¬μš©μ΄ κ°€λŠ₯ν•˜λ‹€. showName()은 λ³„λ„μ˜ μ§€μ—­λ³€μˆ˜κ°€ μ—†μ§€λ§Œ λ‚΄λΆ€ν•¨μˆ˜λŠ” μ™ΈλΆ€ν•¨μˆ˜μ— μ ‘κ·Όν•  κΆŒν•œμ„ κ°€μ§€κ³  μžˆμœΌλ―€λ‘œ, name이 μ •μƒμ μœΌλ‘œ 좜λ ₯될 것이닀. λ§Œμ•½ nameμ΄λΌλŠ” λ‹€λ₯Έ λ³€μˆ˜κ°€ λ‚΄λΆ€ ν•¨μˆ˜μ— μžˆλ‹€λ©΄, κ·Έ λ³€μˆ˜λ₯Ό μš°μ„ μ μœΌλ‘œ μ‚¬μš©ν•  것이닀.

Lexical은 λ³€μˆ˜κ°€ μ‚¬μš©κ°€λŠ₯ν•œ λ²”μœ„λ₯Ό κ²°μ •ν•˜κΈ° μœ„ν•΄ μ†ŒμŠ€μ½”λ“œ λ‚΄μ—μ„œ λ³€μˆ˜κ°€ μ„ μ–Έλœ μœ„μΉ˜λ₯Ό μ‚¬μš©ν•œλ‹€λŠ” 것을 λ§ν•œλ‹€. λ”°λΌμ„œ λ‚΄λΆ€ ν•¨μˆ˜λ“€μ€ κ·Έλ“€μ˜ μ™ΈλΆ€ 유효 λ²”μœ„ λ‚΄μ—μ„œ μ„ μ–Έλœ λ³€μˆ˜λ“€μ— μ ‘κ·Όν•  κΆŒν•œμ„ κ°€μ§„λ‹€.

ν΄λ‘œμ €λž€ 무엇인가

function hello() {
  var name = 'yceffort'
  function showName() {
    alert(`hello, ${name}`)
  }
  return showName
}

let sayHello = hello()
sayHello()

이 μ „κ³Ό μ™„μ „νžˆ λ˜‘κ°™μ€ κ²°κ³Όλ₯Ό 보일 것이닀. 차이점은, hello()κ°€ λ‚΄λΆ€ ν•¨μˆ˜ showNameλ₯Ό λ¦¬ν„΄ν–ˆλ‹€λŠ” 것, 그리고 κ·Έλ ‡κ²Œ λ¦¬ν„΄ν•œ 정보λ₯Ό sayHelloλ³€μˆ˜μ— μ €μž₯ν–ˆλ‹€λŠ” 것이닀. 얼핏보면 잘 이해가 λ˜μ§€ μ•ŠλŠ” λͺ¨μŠ΅μ΄λ‹€. hello()λŠ” showName()λ§Œμ„ λ¦¬ν„΄ν–ˆλŠ”λ°, κ³„μ†ν•΄μ„œ nameλ³€μˆ˜μ— μ ‘κ·Όν•˜κ³  있기 λ•Œλ¬Έμ΄λ‹€.

κ·Έ μ΄μœ λŠ”, μžλ°”μŠ€ν¬λ¦½νŠΈκ°€ ν•¨μˆ˜λ₯Ό λ¦¬ν„΄ν• λ•Œ, λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜κ°€ ν΄λ‘œμ €λ₯Ό μƒμ„±ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. ν΄λ‘œμ €λŠ” ν•¨μˆ˜μ™€ ν•¨μˆ˜κ°€ μ„ μ–Έλœ μ–΄νœ˜μ  ν™˜κ²½μ˜ 쑰합이닀. (ν•¨μˆ˜κ°€ μ„ μ–Έλœ ν™˜κ²½μ„ κΈ°μ–΅ν•œλ‹€.) μ—¬κΈ°μ—μ„œ ν™˜κ²½μ€, ν΄λ‘œμ €κ°€ μƒμ„±λœ μ‹œμ μ— μœ νš¨λ²”μœ„λ‚΄μ— μžˆλŠ” λͺ¨λ“  μ§€μ—­λ³€μˆ˜λ‘œ κ΅¬μ„±λœλ‹€. (λ‚΄λΆ€ ν•¨μˆ˜κ°€ μ™ΈλΆ€ ν•¨μˆ˜μ˜ λ³€μˆ˜μ— μ ‘κ·Όν•  수 μžˆμ—ˆκΈ° λ•Œλ¬Έμ— κ·Έ λ³€μˆ˜λ“€μ„ κΈ°μ–΅ν•˜λŠ” 것)

function add(x) {
  var y = 1
  return function (z) {
    y = 100
    return x + y + z
  }
}

// ν΄λ‘œμ € μ„ μ–Έ
let add5 = add(5)
let add10 = add(10)

console.log(add5(2))
console.log(add10(2))

addν•¨μˆ˜λŠ”, xλ₯Ό 인자둜 λ°›μ•„μ„œ μƒˆλ‘œμš΄ λ‚΄λΆ€ ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•œλ‹€. 이 λ‚΄λΆ€ ν•¨μˆ˜λŠ” zλ₯Ό λ°›μ•„μ„œ x+y+zλ₯Ό λ°˜ν™˜ν•œλ‹€. add5와 add10은 λͺ¨λ‘ ν΄λ‘œμ €λ‹€. 이 두 ν•¨μˆ˜μ˜ κ²°κ³ΌλŠ” μ–΄λ–»κ²Œ 될까?

첫번째 μ„ μ–Έ let add5 = add(5)μ—μ„œ 일단 xκ°€ 5둜 할당이 λ˜μ—ˆλ‹€. 그리고 λ‘λ²ˆμ§Έ add5(2)μ—μ„œλŠ” zκ°€ 2둜 할당이 λ˜μ—ˆλ‹€. 그리고 yκ°€ 두ꡰ데 할당이 λ˜μ–΄μžˆμœΌλ―€λ‘œ, λ‚΄λΆ€λ₯Ό μš°μ„ μ‹œν•˜μ—¬ yλŠ” 100이닀. λ”°λΌμ„œ x+y+z=5+100+2=107x+y+z=5+100+2=107 이 λœλ‹€. λ§ˆμ°¬κ°€μ§€λ‘œ, add10은 x+y+z=10+100+2=112x+y+z=10+100+2=112κ°€ λœλ‹€.

본질적으둜, 이 λ‘κ°œλŠ” 같은 ν•¨μˆ˜μ˜ 본문을 μ •μ˜ν•˜μ§€λ§Œ, μ„œλ‘œ λ‹€λ₯Έ ν™˜κ²½μ„ μ €μž₯ν•œλ‹€. μ΄λŠ” ν΄λ‘œμ €κ°€ λ¦¬ν„΄λœ 후에도 μ™ΈλΆ€ ν•¨μˆ˜μ˜ λ³€μˆ˜μ— 접근이 κ°€λŠ₯ν•˜λ‹€λŠ” 것을 보여주며, λ‹¨μˆœνžˆ κ°’ ν˜•νƒœλ‘œ μ „λ‹¬λ˜λŠ” 것이 μ•„λ‹ˆλΌλŠ” 것을 μ˜λ―Έν•œλ‹€.

μ–΄λ””λ‹€ μ“ΈκΉŒ

ν΄λ‘œμ €λŠ” μ–΄νœ˜μ μΈ ν™˜κ²½κ³Ό 데이터λ₯Ό μ‘°μž‘ν•˜λŠ” ν•¨μˆ˜λ₯Ό μ—°κ΄€μ‹œμΌœ μ£ΌκΈ° λ•Œλ¬Έμ— μœ μš©ν•˜λ‹€. μ΄λŠ” 객체가 μ–΄λ–€ 데이터 (속성)κ³Ό κ·Έ λ©”μ†Œλ“œλ₯Ό μ—°κ΄€μ‹œν‚¨ λ‹€λŠ” μ μ—μ„œ 객체지ν–₯ ν”„λ‘œκ·Έλž˜λ°κ³Ό 같은 λ§₯락에 μžˆλ‹€. λ”°λΌμ„œ, 단 ν•˜λ‚˜μ˜ λ©”μ†Œλ“œ λ§Œμ„ κ°€μ§€κ³  μžˆλŠ” 객체λ₯Ό 일반적으둜 μ‚¬μš©ν•˜λŠ” λͺ¨λ“  곳에 ν΄λ‘œμ €λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

μ΄λŠ” ν”„λ‘ νŠΈμ—”λ“œ μžλ°”μŠ€ν¬λ¦½νŠΈ μ΄λ²€νŠΈμ—μ„œ ν”νžˆ λ³Ό 수 μžˆλ‹€. 사전에 λͺ‡κ°€μ§€ λ™μž‘μ„ μ •μ˜ν•œ 후에, μ‚¬μš©μžκ°€ 이벀트λ₯Ό 트리거 ν•˜λ©΄ 이 λ™μž‘λ“€μ„ μ—°κ²°ν•˜λŠ”λ° μ΄λŠ” μ΄λ²€νŠΈμ— μ‘λ‹΅ν•˜μ—¬ μ‹€ν–‰λ˜λŠ” 단일 ν•¨μˆ˜λ‹€.

function makeFontSize(size) {
  return function () {
    document.body.style.fontSize = size + 'px'
  }
}

let size12 = makeFontSize(12)
let size14 = makeFontSize(14)
let size16 = makeFontSize(16)

document.getElementById('size-12').onclick = size12
document.getElementById('size-14').onclick = size14
document.getElementById('size-16').onclick = size16

프라이빗 λ©”μ†Œλ“œλ₯Ό ν‰λ‚΄λ‚΄λŠ” 것도 κ°€λŠ₯ν•˜λ‹€. 프라이빗 λ©”μ†Œλ“œλŠ” μ½”λ“œμ— μ œν•œμ μΈ μ ‘κ·Όλ§Œ ν—ˆμš©ν•  수 있고, μ „μ—­ λ„€μž„μŠ€νŽ˜μ΄μŠ€λ₯Ό κ΄€λ¦¬ν•˜λŠ” 방법을 μ œκ³΅ν•˜μ—¬ λΆˆν•„μš”ν•œ λ©”μ†Œλ“œκ°€ 곡용 μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν˜Όλž€μŠ€λŸ½κ²Œ λ§Œλ“€μ§€ μ•Šλ„λ‘ ν•  수 μžˆλ‹€.

let counter = (function () {
  let privateCounter = 0
  function change(val) {
    privateCounter += val
  }

  return {
    increment: function () {
      change(1)
    },
    decrement: function () {
      change(-1)
    },
    value: function () {
      return privateCounter
    },
  }
})()

counter.increment()
counter.value()
counter.increment()
counter.increment()
counter.decrement()
counter.value()

change() privateCounterλŠ” λͺ¨λ‘ 읡λͺ…ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ μƒμ„±λ˜μ—ˆκΈ° λ•Œλ¬Έμ— μ ‘κ·Όν•  수 μ—†λ‹€. 이 읡λͺ…ν•¨μˆ˜μ—μ„œ μ ‘κ·Όν•  수 μžˆλŠ”κ±΄ 읡λͺ…λž˜νΌμ—μ„œ λ°˜ν™˜λœ μ„Έκ°œμ˜ νΌλΈ”λ¦­ν•¨μˆ˜ increment() decrement() value() 뿐이닀. μœ„ 처럼 μ¦‰μ‹œμ‹€ν–‰μ΅λͺ…ν•¨μˆ˜κ°€ μ•„λ‹ˆλΌ λ³„λ„μ˜ ν•¨μˆ˜λ‘œ λ§Œλ“€μ–΄μ„œ λ”°λ‘œ μ“΄λ‹€λ©΄, 객체지ν–₯ ν”„λ‘œκ·Έλž˜λ°μ˜ 은닉과 μΊ‘μŠν™” 같은 이점듀을 얻을 수 μžˆλ‹€.

λ£¨ν”„μ—μ„œμ˜ ν΄λ‘œμ €

이 ν•¨μˆ˜λŠ” μƒκ°μ²˜λŸΌ μž‘λ™ν•˜μ§€ μ•ŠλŠ”λ‹€. κ·Έ μ΄μœ λŠ” onfcus에 μ—°κ²°λœ ν•¨μˆ˜κ°€ ν΄λ‘œμ €μ΄κΈ° λ•Œλ¬Έμ΄λ‹€. 이 ν΄λ‘œμ €λŠ” setupHelp() ν•¨μˆ˜λ²”μœ„μ—μ„œ 캑쳐된 ν™˜κ²½μœΌλ‘œ κ΅¬μ„±λœλ‹€. λ£¨ν”„μ—μ„œ μ„Έκ°œμ˜ μ„Έκ°œμ˜ ν΄λ‘œμ €κ°€ λ§Œλ“€μ–΄μ‘Œμ§€λ§Œ, 각 ν΄λ‘œμ €λŠ” 값이 λ³€ν•˜λŠ” λ³€μˆ˜ item.helpκ°€ μžˆλŠ” 단일 ν™˜κ²½μ„ κ³΅μœ ν•œλ‹€. λ”°λΌμ„œ κ³„μ†ν•΄μ„œ λ§ˆμ§€λ§‰ λ³€μˆ˜λ₯Ό κ°€λ₯΄ν‚€κ²Œ λ˜λŠ” 것이닀.

첫번째 ν•΄κ²°λ°©μ•ˆμ€ showHelp()λ₯Ό κ°μ‹ΈλŠ” ν΄λ‘œμ €λ₯Ό λ§Œλ“œλŠ” 것이닀.

showHelp()λŠ” μ—¬μ „νžˆ 단일 ν™˜κ²½μ—μ„œ μž‘λ™ν•˜μ§€λ§Œ, makeHelpCallback()λŠ” 맀번 μƒˆλ‘œμš΄ ν΄λ‘œμ €λ₯Ό λ§Œλ“€μ–΄μ„œ μƒˆλ‘œμš΄ ν™˜κ²½μ„ ν˜•μ„±ν•œλ‹€.

μ•„λ‹ˆλ©΄ μ¦‰μ‹œμ‹€ν–‰μ΅λͺ…ν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄μ„œ for κ΅¬λ¬Έλ‚΄μ˜ ν™˜κ²½μ΄ λ³„λ‘œ μ¦‰μ‹œλ‘œ μ‹€ν–‰λ˜κ²Œ ν•˜λŠ” 방법도 μžˆμ„ 수 μžˆλ‹€.

λ°˜λ“œμ‹œ for ꡬ문 λ‚΄μ˜ λ‘œμ§μ„ μ¦‰μ‹œμ‹€ν–‰ν•¨μˆ˜λ‘œ κ°μ‹Έμ„œ λ³„λ„μ˜ ν™˜κ²½μœΌλ‘œ κ΅¬μ„±λ˜κ²Œ ν•΄μ•Ό ν•œλ‹€.

μ•„λ‹ˆλ©΄ let을 μ‚¬μš©ν•˜μ—¬ itemλ³€μˆ˜μ˜ λ²”μœ„μžμ²΄λ₯Ό forλ¬Έ λ‚΄λ‘œ μ œν•œν•  μˆ˜λ„ μžˆλ‹€.

μ„±λŠ₯

ν΄λ‘œμ €κ°€ ν•„μš”ν•˜μ§€ μ•Šμ€ μž‘μ—…μ— λ‹€λ₯Έ ν•¨μˆ˜λ‚΄μ—μ„œ ν•¨μˆ˜λ₯Ό λΆˆν•„μš”ν•˜κ²Œ 계속 μ„ μ–Έν•˜κ³  μž‘μ„±ν•˜λŠ” 것은 μ„±λŠ₯에 μ•…μ˜ν–₯을 λ―ΈμΉœλ‹€. 예λ₯Ό λ“€μ–΄, μƒˆλ‘œμš΄ κ°μ²΄λ‚˜ 클래슀λ₯Ό μƒμ„±ν• λ•Œ λ©”μ†Œλ“œλ₯Ό 객체 μƒμ„±μžμ— μ •μ˜ν•˜λŠ” 것 λ³΄λ‹€λŠ” 객체의 ν”„λ‘œν† νƒ€μž…μ— μ—°κ²°ν•΄μ•Ό ν•œλ‹€.

μ•ˆμ’‹μ€ 예

function MyObject(name, message) {
  this.name = name.toString()
  this.message = message.toString()
  this.getName = function () {
    return this.name
  }

  this.getMessage = function () {
    return this.message
  }
}

μ΄λ ‡κ²Œ ν•˜κΈ°λ³΄λ‹€λŠ” prototype에 μ •μ˜ν•˜λŠ” 것이 훨씬 λ‚«λ‹€.

function MyObject(name, message) {
  this.name = name.toString()
  this.message = message.toString()
}
MyObject.prototype.getName = function () {
  return this.name
}
MyObject.prototype.getMessage = function () {
  return this.message
}
function MyObject(name, message) {
  this.name = name.toString()
  this.message = message.toString()
}
;(function () {
  this.getName = function () {
    return this.name
  }
  this.getMessage = function () {
    return this.message
  }
}).call(MyObject.prototype)

μ΄λ ‡κ²Œ μ“΄λ‹€λ©΄ 쒀더 μ„Ήμ‹œν•΄ 보일 것이닀.