프로그래밍 공부/Javascript

원시/참조 자료형 ,스코프, 클로저 / TIL 13일차

Kevinkb 2021. 9. 8. 18:49

TIL

원시 자료형과 참조 자료형

원시 자료형과 참조 자료형의 구분이 필요한 이유

메모리에 데이터를 저장할 때, 크기가 동적인 데이터가 들어오는 경우 데이터가 언제 늘어나고 줄어들지 모르기 때문에 고정된 데이터 공간을 사용하는 것은 비효율적이다. 그렇기 때문에, 크기가 상황에 따라서 커졌다가 작아지는 특별한 데이터 저장소(Heap)를 마련하여 따로 관리한다. 그래서 원시 자료형이 할당될 때에는 변수에 값(value) 자체가 담기고, 참조 자료형이 할당될 때는 Heap에 저장된 보관함의 주소(reference)가 담긴다.

// 원시 자료형일 경우
let a = 2;
let b = a;

a = 3;
console.log(a) // 3;
console.log(b) // 2;

// 참조 자료형일 경우
let e = [10, 20];
let f = e;

f[0] = 50;
console.log(e) // [50, 20];
console.log(f) // [50, 20];

스코프

의미: 변수 접근 규칙에 따른 유효 범위
적용 범위: 코드 블록(중괄호) 또는 함수에 의해 나눠진다.
바깥쪽 스코프에서 선언한 변수는 안쪽 스코프에서 사용 가능, 반대는 불가능
(안쪽에서 선언한 변수는 바깥쪽 스코프에서 사용할 수 없다.)

주요 규칙

중첩 규칙: 스코프는 중첩이 가능
block scope: 중괄호를 기준으로 범위가 구분. 화살표 함수는 블록 스코프로 취급
function scope: function 키워드가 등장하는 함수 선언식 및 함수 표현식은 함수 스코프

전역 스코프와 지역 스코프

전역 스코프: 가장 바깥쪽의 스코프
지역 스코프: 전역 스코프를 제외한 모든 스코프

※전역 변수와 지역 변수간의 우선 순위

지역 변수는 전역 변수보다 더 높은 우선순위를 가진다. 동일한 변수 이름으로 인해 바깥쪽 변수가 안쪽 변수에 의해 가려지는(shadow) 현상을 쉐도잉(variable shadowing)이라고 한다.

var 키워드

var 키워드는 블록스코프를 무시하고, 함수 스코프만 따른다. 하지만 화살표 함수의 블록 스코프는 무시하지 않는다. 전역 변수를 var로 선언하는 것은 브라우저의 내장 기능을 사용하지 못하게 만들 수도 있다. 그렇기 때문에, 전역 변수를 많이 만드는 것은 그다지 좋은 선택이 아니며 전역 변수를 최소화하는 것은 side effect를 줄이는 좋은 방법이다. var 키워드 대신 let과 const를 사용하자.

전역 객체(window)

브라우저의 창(window)을 의미하는 객체이지만, 이와 별개로 전역 영역을 담고 있기도 하다.


클로저(closure)

클로저의 정의

클로저는 함수와 그 함수가 선언된 어휘적(렉시컬 lexical) 환경과의 조합. 이 환경은 클로저가 생성 된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.

※ 어휘적(렉시컬) 스코프
자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다. 즉, 함수를 정의한 환경을 기준(어휘적 환경)으로 변수를 조회한다.    

클로저 함수

클로저 함수는 외부함수의 컨텍스트(변수)에 접근할 수 있는 내부함수를 뜻한다.
외부함수의 실행이 종료된 후에도, 클로저 함수는 외부함수의 스코프, 즉, 함수가 선언된 어휘적 환경에 접근할 수 있다.(어휘적 환경의 유효 범위 내에 있는 지역 변수를 사용 가능하다.)    

클로저의 특징

  1. 함수를 리턴하는 함수가 클로저의 형태를 만든다.

  2. 리턴하는 함수에 의해 스코프가 구분된다(내부 함수는 외부 함수에 선언된 변수에 접근 가능하다). 클로저의 핵심은 스코프를 이용해서, 변수의 접근 범위를 닫는(closure; 폐쇄) 데에 있다. 따라서, 함수를 리턴하는 것만큼이나, 변수가 선언된 환경이 중요하다.

  3. 외부함수의 스코프를 참조하더라도 외부함수보다 일찍 소멸되면 클로저의 본질에 부합하지 않는다.    

클로저의 활용

1. 데이터를 보존하는 함수
divMaker 함수는 'div'라는 문자열을 tag 라는 변수에 담아두고 있으며, anchorMaker 함수는 'a'라는 문자열을 tag에 담아두고 있다. 클로저는 이처럼 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게 해준다.

const tagMaker = tag => content => `<${tag}>${content}</${tag}`;

const divMaker = tagMaker('div');
divMaker('hello')    // '<div>hello</div>'
divMaker('code')   //  '<div>code</div>'

const anchorMaker = tagMaker('a');
anchorMaker('go')  // '<a>'go'</a>'
anchorMaker('we')  // '<a>'we'</a>'

2. 정보의 접근 제한(캡슐화)
value 라는 변수는 스코프 규칙에 의해 어떤 경우에도 value는 직접 수정이 불가능하다. 이것이 정보의 접근 제한(캡슐화)이다.

const makeCounter = () => {
  let value = 0;

  return {
    increase: () => {
value = value + 1
},
decrease: () => {
value = value - 1
},
getValue: () => value

  }
}
const counter1 = makeCounter();
counter1  // { increase: f, decrease: f, getValue: f }

 
3. 모듈화를 통한 재활용
함수 재사용성을 극대화하여, 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것을 모듈화라고 한다.
클로저를 통해 데이터와 메소드를 같이 묶어서 다룰 수 있다. 즉, 클로저는 모듈화에 유리하다.

const counter1 = makeCounter();
counter1.increase();
counter1.increase();
counter1.decrease();
counter1.getValue();  //  1

const counter2 = makeCounter();
counter2.decrease();
counter2.decrease();
counter2.decrease();
counter2.getValue();  //  -3

makeCounter에 의해 리턴된 객체는, makeCounter를 실행할 때에 선언되는 value 값을 각자 독립적으로 가지게 된다. 따라서 counter1에서의 value와 counter2에서의 value는 서로에게 영향을 끼치지 않고, 각각의 값을 보존할 수 있다.    

클로저의 단점

일반 함수였다면 함수 실행 종료 후 가비지 컬렉션 대상이 되었을 객체가, 클로저 패턴에서는 메모리 상에 남아 있게 된다. 외부 함수 스코프가 내부함수에 의해 언제든지 참조될 수 있기 때문, 따라서 클로저를 남발할 경우 퍼포먼스 저하가 발생할 수도 있다.하지만 모던 자바스크립트 엔진은 최적화가 잘 되어 있기 때문에 클로저가 참조하고 있지 않는 식별자는 기억하지 않는다. 즉, 클로저의 메모리 점유는 필요한 것을 기억하기 위한 것이므로 걱정할 대상은 아니다.

※자바스크립트는 가비지 컬렉션을 통해 메모리 관리를 한다. 객체가 참조 대상이 아닐 때, 가비지 컬렉션에 의해 자동으로 메모리 할당이 해제된다.

'프로그래밍 공부 > Javascript' 카테고리의 다른 글

DOM / TIL 15~16일차  (0) 2021.09.10
this / TIL 14일차  (0) 2021.09.09
TIL(배열, 객체) 10일차  (0) 2021.09.03
계산기 구현 / TIL 7일차  (0) 2021.08.31
TIL 6일차 2021.08.30  (0) 2021.08.30