- Today
- Total
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- apollo
- Firebase
- Flutter
- javascript
- nestjs
- error
- Coin
- graphql
- rtk
- 채팅
- 차트
- 코인
- typescript
- 코인차트
- 주식
- react
- 주식차트
- API
- 3주차
- typeorm
- 차트구현
- nextjs
- 에러
- chart
- websocket
- 항해99
- 차트만들기
- 리액트
- 비전공자
- Redux
Act99 기술블로그
[클로저 & 프로토타입] - 클로저와 프로토타입에 대한 이해 본문
클로저란 무엇인가?
MDN (Mozilla Developer Netwrok)에 따르면,
“클로저는 함수와 함수가 선언된 어휘적 환경의 조합입니다.”
클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지를 이해해야 합니다.
정말 이해가 안되는 설명입니다. 이걸 쉽게 풀이하자면,
클로저란 독립적이고 자유로운 변수를 가리키는 함수입니다.
또한, 클로저 안에서 정의된 함수는 만들어진 환경을 기억합니다.
이것도 역시 이해가 안됩니다. 그럼 바로 코드로 보시겠습니다.
function getClosure() {
let text = 'variable 1';
return function() {
return text;
};
}
let closure = getClosure();
console.log(closure());
// 'variable 1'
분명히 저희가 배운 바에 따르면, return 을 하게되면 함수가 종료되야 합니다.
즉, function() {return text} 를 뱉어야겠죠.
또한, function(){return text} 에서 text는 getCloser() 함수가 종료되면 변수가 사라져야 합니다.
하지만 getClosure() 의 리턴값인 function() {return text} 가 아닌,
function() {return text} 함수의 return 값인 text 를 뱉고있죠.
let base = "Hello, ";
function sayHelloTo(name) {
let text = base + name;
return function () {
console.log(text);
};
}
let hello1 = sayHelloTo("예진");
let hello2 = sayHelloTo("제열");
let hello3 = sayHelloTo("민재");
hello1(); // 'Hello, 예진'
hello2(); // 'Hello, 제열'
hello3(); // 'Hello, 민재'
이 코드를 보시면 text 라는 변수 자체가 여러번 생성되었으며, sayHelloTo 함수를 호출하는 hello1, hello2, hello3 은 서로 다른 환경을 가지고 있는 것을 볼 수 있습니다.
클로저를 통한 은닉화
일반적으로 JavaScript 에서 객체지향 프로그래밍을 말한다면 Prototype을 통해 객체를 다루는 것을 말합니다.
프로토타입
그럼 포로토타입이란 무엇일까요? 자바스크립트는 클래스는 있지만 자바에서의 클래스와 같은 개념은 아니며, 기존 객체를 복사하여 새로운 객체를 생성하는 프로토타입 기반의 언어입니다. 근데 왜 저희는 자바스크립트를 쓰면서 프로토타입을 발견할 수 없을까요? 이 이유는 자바스크립트를 사용할 시 기본적으로 프로토타입이 숨겨저있지만 참조되어 있기 때문입니다.
function Rectangle2(height, width) {
return function () {
return height * width;
};
}
let rec = new Rectangle2(10, 10);
보시다시피 선언하지 않아도 prototype 이 존재하고 constructor 을 선언하지 않아도 prototype 안에 constructor , 즉 생성자가 만들어져 있는 것을 볼 수 있습니다. 이처럼 자바스크립트는 프로토타입이 자동적으로 내재되어 있으며, 따라서 기존 객체를 복사해 새로운 객체를 생성하는 프로토타입 언어라는 것을 알 수 있습니다.
저희가 알고리즘 테스트에서 자주 썼던 함수인 Set 의 경우 프로토타입은 이렇습니다.
그리고 프로토 타입의 구조를 조금 명확히 보려면, 클래스 문법을 쓰면 되는데, 다음 코드를 통해 어떻게 프로토타입이 형성되는지 보실 수 있습니다.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
get area() {
return this.calArea();
}
calArea() {
return this.height * this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100
console.log(square.height); // 10
console.log(square.calArea()); //100
let cal = square.calArea;
console.log(cal); //[Function: calArea]
let cal2 = square.calArea();
console.log(cal2); //100
보시다시피 Rectangle 안에는 프로토타입이 총 3가지가 있죠.
첫 번째는 constructor, 두 번째는 area 입니다. 세 번째는 calArea() 입니다.
area는 calArea()의 리턴값을 반환하는 함수이며, calArea는 순수함수입니다.
따라서 그냥 calArea를 부르면 타입을 가져오고, calArea()를 부르면 area와 같이 리턴값을 가져오게 되죠.
다시 돌아와 클로저를 통한 은닉화를 설명하자면
다시 돌아와 Prototype을 통한 객체를 만들 때, 주요한 문제 중 하나는 private variables에 대한 접근 권한 문제입니다.
코드를 보시면
function Hello(name) {
this._name = name;
}
// say 함수를 만들어준다.
Hello.prototype.say = function () {
console.log("Hello, " + this._name);
};
var hello1 = new Hello("민재");
var hello2 = new Hello("예진");
var hello3 = new Hello("제열");
hello1.say(); // 'Hello, 민재'
hello2.say(); // 'Hello, 예진'
hello3.say(); // 'Hello, 제열'
hello1._name = "anonymous";
hello1.say(); // 'Hello, anonymous'
위에서 Hello()로 생성된 객체들은 모두 _name 이라는 변수를 가지게 되며, 변수명 앞에 underscore(_)를 포함했기 때문에 일반적인 Javascript 네이밍 컨벤션을 생각했을 때, 이 변수는 Private variable으로 쓰고싶다는 의도를 알 수 있습니다. 하지만 실제로는 여전히 외부에서 쉽게 접근 가능하죠.
이 경우에!!!!!!!
클로져를 이용하여 외부에서 변수를 직접 접근하는 것을 제한할 수 있습니다.
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, 민재'
함수 내에서 특정 변수를 지정하여 prop을 받아 Private variable을 만들 수 있는 것이죠.
특별한 인터페이스를 제공하는 것이 아니라면, 여기선 외부에서 _name에 접근할 방법이 없습니다. 이렇게 은닉화를 쉽게 해결할 수 있습니다.
클로저의 성능
클로저는 각자의 환경을 가집니다. 이 환경을 기억하기 위해선 메모리가 소모됩니다. 따라서 클로저 사용이 끝나면 참조를 제거하는 것이 좋습니다.
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, 예진'
// 여기서 메모리를 release 시키기 클로저의 참조를 제거해야 한다.
hello1 = null;
hello2 = null;
hello3 = null;
마무리
그럼 왜 클로저를 사용하는지 정리하자면,
1. 데이터를 보존할 수 있다.
- 클로저 함수는 외부 함수의 실행이 끝나더라도 외부 함수 내 변수를 사용할 수 있으며, 이처럼 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게 하는 폐쇄성을 가진다.
2. 정보의 접근 제한
- 클로저 모듈 패턴을 사용해 객체에 담아 여러 개의 함수를 리턴하도록 만든다.
3. 모듈화에 유리하다.
- 클로저 함수를 각각의 변수에 할당하면, 각자 독립적으로 값을 사용하고 보존할 수 있다.
- 함수의 재사용을 극대화한 함수 하나를 독립적인 부품의 형태로 분리하는 것을 모듈화라 하며, 클로저를 통해 데이터와 메소드를 묶어다닐 수 있기 때문에 클로저는 모듈화에 유리하다.
'개발팁저장소' 카테고리의 다른 글
[항해99 1주일 클론코딩] - 슬랙 (Slack) 클론코딩 (0) | 2022.02.24 |
---|---|
[항해99 미니프로젝트] 1주일간의 프로젝트 (title: 공구리) (0) | 2022.02.17 |
[TDZ(Temporal Dead Zone) 일시적 사각지대란?] (0) | 2022.02.09 |
[항해99 4주차 WIL] ORM & NoSQL vs SQL (0) | 2022.02.04 |
항해99 3주차 / 웹 저장소 (feat. 토큰) (0) | 2022.02.01 |