클로저란?
함수와 함수가 선언되었을 때의 렉시컬 환경(Lexical environment) 의 조합을 의미한다.
클로저는 자바스크립트에서 중요 개념이며 자바스크립트 뿐만 아니라
함수를 객체로 사용하는 대부분의 소프트웨어 언어에서 제공되는 개념이다.
클로저는 자바스크립트에서 함수를 이용하기 위해서 자동으로 제공되는 개념이며
클로저를 위해 개발자가 어떤 코드적인 프로그램을 작성해야 하는 것은 아니고
클로저 개념을 접하면 실행 컨텍스트(Execution Context) 개념과 렉시컬 환경을 이해가 선행되어야 한다.
▼ 실행 컨텍스트
• 실행 컨텍스트란 함수의 실행 환경이며 함수가 실행되기 위한 정보를 가지는 객체이다.
함수가 호출되어 실행되려면 함수에 전달된 매개변수 값, 함수내에 선언된 로컬 변수, 로컬 함수등을 이용해야 하는데
이러한 정보를 가지는 객체가 실행 컨텍스트이다.
• 함수가 호출이 되면 자동으로 그 함수를 위한 실행 컨텍스트가 만들어지고
이 실행 컨텍스트내에 그 함수를위한 매개변수 값, 로컬 변수등이 저장된다고 보면된다. 그래서 함수내에서 매개변수를 참조하거나 로컬의 변수를 참조할 때 자동으로 함수의 실행 컨텍스트내에등록된 것들이 이용되는 구조이다.
• 실행 컨텍스트는 함수 호출 순서대로 스택 구조로 유지된다.
▷ 함수를 위한 컨텍스트 정보가 유지됨으로 전역 위치에 선언한 변수는 모든 함수에서 사용이 가능한 것이며
특정 함수내에 선언한 로컬 변수들은 그 함수 호출이 끝나면 제거됨으로
그 함수가 실행되는 도중에만 사용할 수 있게 되는 것이다.
스택 구조란?
• 흔히 Last In – First Out 으로 어떤 정보가 유지되는 구조를 의미하며
만들어진 순서대로 차곡차곡 쌓아놓았다가 가장 위에 것부터 제거하는 구조이다.
그럼으로 가장 마지막에 추가된 것이 가장 먼저 제거된다고 해서 Last In – First Out 구조라고 한다.
▼ 렉시컬 환경
• 렉시컬(Lexical) 이란 영어 단어 뜻은 ‘허휘’, ‘구문’ 이라는 뜻이 있다.
렉시컬 환경 혹은 렉시컬 스코프라고 하는데 이 의미는 함수가 선언된 위치를 의미하며
즉 함수가 실행되는 동적인 환경이 아닌 코드로 함수를 작성한 위치를 의미한다.
• innerFun() 을 outerFun() 함수 내에 선언했음으로 innerFun() 의 렉시컬 환경은 outerFun() 이다.
//execution context
let x = 10
function oneFun(){
let y = 20
console.log(x, y)
}
function twoFun(){
let z = 30
console.log(x, z)
}
console.log(x)
oneFun()
twoFun()
console.log(x)
▼ 클로저가 왜 필요한가?
• 함수를 선언하고 그 함수를 호출해서 실행시키는 모든 상황에서 클로저가 만들어지지 않는다.
혹은 개발하면서 클로저를 모든 함수 호출시에 고려해야 하는 것은 아니다.
클로저가 필요한 경우는 함수를 호출하는 곳이 함수가 선언된 렉시컬 환경 내부가 아닌 경우이다.
• 위의 경우는 함수의 실행이 자신이 선언된 렉시컬 환경내에서 실행된 경우이며
클로저가 만들어지지 않았는데
• 모든 함수의 실행이 렉시컬 환경에서 실행된다고 볼 수 없다.
• 클로저를 정의할 때 “클로저(Closure) 란 함수와 함수가 선언되었을 때의
렉시컬 환경(Lexical environment)의 조합” 이라고 했을때,
어떤 함수가 그 함수가 선언된 위치 외부에 전달돼, 실행될 때 자신이 선언된 렉시컬 환경의 정보를
이용할 수 있게 클로저가 자동으로 만들어져서 함수에 추가된다.
//outerFun 내의 innerFun 이 선언되어 있고
//innerFun 에서 outerFun 의 정보를 이용하고 있지만(lexical 정보)
//execution context 정보만으로 충분함으로 "closuer 는 필요가 없는 상황"
function outerFun(){
let y = 20
function innerFun(){
let z = 30
console.log(x, y, z)
}
innerFun()
}
outerFun()
//closure 가 필요한 상황
//함수의 lexical 정보가 따로 유지되어야 하는 상황
function outerFun(){
let y = 20
function innerFun(){
let z = 30
console.log(x, y, z)
}
return innerFun
}
let resultFun = outerFun()
resultFun()
캡슐화(Encapsulation)
• 캡슐화는 객체지향 프로그래밍에 자주 등장하는 용어이다.
캡슐내에 내용을 담듯이 관련된 프로퍼티, 메서드를 하나로 묶어서 작성한다는 의미에서 캡슐화라고 부른다.
자바스크립트 측면에서는 생성자 함수를 이용하거나 클래스를 이용해 캡슐화 프로그램을 작성한다.
• 캡슐화를 하는 주된 이유는 두가지이며
관련된 것을 묶어서 하나의 프로그램 단위로 개발하고 관리해 개발 생산성 및 유지보수성을 증대하겠다는이유와
캡슐로 내용을 묶고 외부에 캡슐 내의 내용을 감추어 정보 은닉을 하고자 하는 목적이다.
"use strict"
//test1- 아래의 생성자 함수, 클래스는 전혀 캡슐화 되어 있지 않다
//외부에서 객체의 데이터를 가지는 변수멤버를 직접 접근하고 있다..
//유지보수성이 떨어진다..
//생성자 함수..
// function UserFunction(){
// this.name = '홍길동'
// this.age = 10
// }
// let obj1 = new UserFunction()
// obj1.name = '김길동'
// obj1.age = 20
// console.log(obj1.name, obj1.age)
// class UserClass {
// constructor(){
// this.name = '홍길동'
// this.age = 10
// }
// }
// let obj2 = new UserClass()
// obj2.name = '이길동'
// obj2.age = 30
// console.log(obj2.name, obj2.age)
//test2 - 캡슐화를 적용
//멤버변수는 외부에 노출 안되게 함
//변수 값 변경 및 획득이 필요하다면 함수를 제공하고
//그 함수를 오픈해서 함수를 이용해서 변수를 이용하게 함
function UserFunction(){
//생성자 함수내에 변수가 선언되었지만 this. 으로 선언하지 않았다
//생성되는 객체에 담기지 않는다. 로컬 변수이며 외부에서는 이용못한다
let name = '홍길동'
let age = 10
//이 값을 객체에 유지했다가 함수를 통해 이용하게 함
//외부에서 이용하는 함수내에 lexical 정보로 유지되게 함
this.getName = function(){
return name
}
this.setName = function(value){
name = value
}
this.getAge = function(){
return age
}
this.setAge = function(value){
age = value
}
}
let obj1 = new UserFunction()
obj1.setName('김길동')
obj1.setAge(20)
console.log(obj1.getName(), obj1.getAge())
class UserClass {
#name = '홍길동'
#age = 10
getName(){
return this.#name
}
setName(value){
this.#name = value
}
getAge(){
return this.#age
}
setAge(value){
this.#age = value
}
}
let obj2 = new UserClass()
obj2.setName('이길동')
obj2.setAge(30)
console.log(obj2.getName(), obj2.getAge())
즉시 실행 함수
• 즉시 실행 함수란 선언하자 마자 호출해 사용하는 함수를 의미한다.
일반적으로 함수는 선언한 후 필요할 때 해당 함수를 반복적으로 호출해서 사용한다.
그러므로 함수 이름을 지정해야 하고 그 이름으로 호출해 사용되는데 즉시 실행 함수는 이름을 가지지 않는다.
결국 선언과 동시에 호출하지 않는 한 다시 호출할수 없게 되며
즉시 실행 함수는 ( ) 안에 function() { } 형태로 선언되며 이렇게 선언하자 마자 ( ) 로 호출하여 이용된다.
• 즉시 실행 함수가 이용되는 대부분의 이유는 어떤 변수, 함수가 이용되는 범위를 즉시 실행 함수로 묶어 즉시 실행 함수 내에서만 사용되게 하고자 하는 목적이다.
즉 변수, 함수가 전역에서 사용되지 못하고 즉시 실행 함수내에서만 사용되게 하고자 하는 목적이다.
• 즉시 실행 함수를 이용해 특정 영역을 선언하고 그 영역내에서만 변수, 함수가 이용되게 하는 기법은 여러상황에서 유용한데 하나의 예가 라이브러리 코드 개발자 입장이다.
라이브러리는 그 라이브러리를 누군가의 코드에서 이용한다는 개념인데 라이브러리내에 선언된 변수, 함수명이 그 라이브러리를 이용하는 곳까지 영향을 미쳐 의도치 않게 문제가 발생할 수 있다.
라이브러리를 만들면서 라이브러리내에서만 사용하고자 하는 변수, 함수가 있다면 이후 이 라이브러리를이용하는 곳에 영향을 주지 않게 하기 위해서 즉시 실행함수로 라이브러리 내에서만 사용하게 할 수 있다.
• 즉시 실행 함수가 이용되는 유용한 또다른 예가 여러 함수에서 이용되는 변수를 선언하는데
그 변수가 특정함수들 내에서만 사용되게 하고자 할 때 이용할 수 있다.
"use strict"
//일반 함수 선언과 이용
function myFun(name) {
console.log('myFun call')
}
//선언과 호출은 별개
//원하는 만큼 반복 호출 가능
myFun('홍길동');
myFun('김길동');//; 으로 명시적
//즉시 실행 함수
//선언과 동시에 호출. 이름을 가지지 않기 때문에 반복 호출 불가
(function (name) {
console.log('Hello')
})('홍길동');
//특정 변수, 함수의 이용 범위를 한정짓고 싶을때 주로 라이브러리 코드 내에서
(function () {
let data = 10
function fun1() { }
})()
// data = 20
// fun1()
//특정 변수가 있는데 몇몇 함수에서만 사용되게 강제하고자 할때
// let count = 0
// function increment(){
// count++
// }
// function decrement(){
// count--
// }
// increment()
// increment()
// decrement()
// console.log(count)//1
// //increment, decrement 에서 사용하는 데이터가 코드 어디선가
// //다른 의미로 사용이 되는것을 방지할 수 없게 된다
// count = 100
// increment()
// console.log(count)//101
const counter = (function(){
let count = 0
return function(argFun){
count = argFun(count)
return count
}
})()
let increment = (no) => ++no
let decrement = (no) => --no
console.log(counter(increment))//1
console.log(counter(increment))//2
console.log(counter(decrement))//1
//뭔가 잘못된(?)
let count = 100
console.log(counter(increment))//2
'BootCamp Review' 카테고리의 다른 글
2024.10.04 (Day 20) - Ajax, Axios / Web APIs (0) | 2024.10.07 |
---|---|
2024.10.02-04 (Day 19,20) - JavaScript 비동기/ Web APIs (0) | 2024.10.04 |
2024.10.01 (Day 18) - JavaScript OOP / 클래스(class) (0) | 2024.10.01 |
2024.09.26-30 (Day 15,16,17) - JavaScript OOP / 다양한 OOP 기법 (0) | 2024.09.30 |
2024.09.25-26 (Day 14,15) - JavaScript OOP / 프로토 타입 (0) | 2024.09.26 |