티스토리 뷰

with 하루

내 질문에 대한 내용 정리

Q1.

스코프와 스코프 체인은 언제 결정되는 것 일까요?

  • 상위 스코프(외부 렉시컬 환경에 대한 참조)는 함수 정의가 평가되는 시점에 함수가 정의된 위치에 의해 결정된다.

Q2.

자바스크립트에서 지역 스코프를 가지는 키워드들은 무엇이 있을까요? (ex. function 등)

  • 전역 스코프
  • function, class
  • if, for, while, try / catch

📌 주의) 객체 리터럴은 지역 스코프를 가지지 않는다. (코드 블록이 아니기 때문)

Q3.

내부 함수는 무조건 클로저라고 할 수 있을까요?

  • 자바스크립트의 모든 함수는 상위 스코프를 기억하므로 이론적으로 모든 함수는 클로저이다.
  • 하지만 일반적으로 모든 함수를 클로저라고 하지는 않는다.
  • 클로저는 내부 함수가 상위 스코프의 식별자를 참조하고 있고 내부 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하는 것이 일반적이다.
// 예외 케이스 1
// 외부 함수보다 내부 함수의 생명 주기가 짧은 경우
function foo() {
  const x = 1;

  function bar() {
    console.log(x);
  }

  bar();
}

foo();
// 예외 케이스 2
// 상위 스코프의 식별자를 참조하지 않는 경우
function foo() {
  const x = 1;

  function bar() {
    const z = 3;
    console.log(z);
  }

  return bar;
}

const bar = foo();
bar();

Q4.

클로저는 상태가 의도치 않게 변경되지 않도록 은닉하는데 자주 사용됩니다. 하지만 일부는 클로저를 잘못 사용하게 되는 경우에 메모리 누수가 일어날 수 있어 사용을 지양한다고 합니다. 클로저를 사용할 때 유의할 점과 메모리 관리를 어떻게 하면 좋을지 이야기를 나눠봐요 😊

클로저는 외부 함수의 지역변수를 사용하기 때문에 메모리를 소모하도록 만든다. 이 때문에 필요성이 사라진 시점에는 참조 카운트를 0으로 만들어 GC가 수거하도록 해야한다.

클로저의 식별자에 null을 할당하여 참조 카운트를 0으로 만들어 메모리를 해제할 수 있다.

let foo = (function () {
  const x = 1;

  const bar = function () {
    console.log(x);
  };

  return bar;
})();

foo();
foo = null;

+) 클로저는 상위 스코프의 일부 식별자만 필요한 경우에도 상위 스코프를 모두 기억해야 하므로 불필요한 메모리가 사용된다?

A. 모던 자바스크립트 엔진은 최적화가 잘 되어 있어 클로저가 참조하고 있지 않는 식별자는 기억하지 앟는다. 즉, 상위 스코프의 식별자 중에서 기억해야 할 식별자만 기억한다.

 

하루 질문에 대한 내용 정리

Q1

전역변수를 사용하면 어디서든지 사용할 수 있어 편하게 느껴지기도 합니다. 이렇게 전역 스코프 또는 전역 네임스페이스에 모든 변수를 선언하는 것은 왜 안티패턴일까요?

  1. 전역 변수는 모든 스코프에서 접근이 가능하기 때문에 어디서 값을 변경시켰는지 파악하기 어렵다.
  2. 전역 실행 컨택스트는 프로그램이 종료될 때까지 생존해 있기 때문에 GC의 대상이 되지 않는다. 즉, 생명 주기가 길다.
  3. 스코프 체인의 종점에 위치하기 때문에 변수를 검색할 때 전역 변수가 가장 마지막에 검색된다. 즉, 전역 변수의 검색 속도가 가장 느리다.
  4. 코드가 길어지고 프로그램이 복잡해지면 전역에 어떤 변수를 선언했는지 기억하기 어렵다.
  5. 변수 이름을 짓기가 어려워진다. 특히, 공통으로 많이 사용되는 변수명 (ex, num, name 등)

Q2

전통적으로 함수 레벨 스코프를 지원(var)해오던 자바스크립트에서 블록 레벨 스코프를 지원(const, let)하게 된 이유는 무엇일까요?

1번 질문에서 살펴본 것 처럼 스코프를 크게 가져가는 것보다는 작게 가져가는 것이 이점이 많기 때문이라고 생각한다. 이 때문에 자바스크립트에 블록 레벨 스코프가 적용되었다고 생각한다. 하지만 기존 var 키워드가 함수레벨 스코프에서 블록 레벨 스코프가 적용되도록 바뀐다면 이전에 작성된 프로그램이 정상 동작하지 않을 수 있으므로 블록 레벨 스코프를 적용하기 위해 let과 const 키워드가 등장했다고 생각한다.

Q3

동적스코프와 정적 스코프(렉시컬 스코프) 중 어떤 상위 스코프 결정 방식이 클린 코드를 작성하고 개발자가 코드를 수월하게 읽는데 도움이 될까요?

  • 대부분의 언어들은 렉시컬 스코프를 사용한다고 한다.
  • 동적 스코프는 프로그램 런타임 중 실행 컨텍스트에 따라 달라진다.
  • 동적 스코프는 함수를 호출한 쪽에서 유연하게 함수를 재활용할 수 있다.
  • 동적 스코프는 개발자가 의도하지 않은 결과를 가질 위험성이 있다.
  • 동적 스코프를 확인하기 위해서는 호출 순서를 따라가야하지만 렉시컬 스코프는 정의된 위치만 확인하면 되므로 가독성이 좋다고 생각한다.

 

기타 인사이트

  • for 문의 변수 선언문에 let 키워드를 사용한 for 문은 코드 블록이 반복해서 실행될 때마다 코드 블록을 위한 새로운 렉시컬 환경을 생성한다. 이는 for 문의 변수 선언 문 및 for 문의 코드 블록 내에서 선언된 지역 변수 등의 값을 유지하기 위함이다. for 문이 끝나고 for 문이 생성한 렉시컬 환경에 대한 참조가 없다면 가비지 컬렉션에 의해 렉시컬 환경은 사라진다.

'스터디 > 하브루타 스터디' 카테고리의 다른 글

9. 프로토타입  (1) 2021.06.06
8. 에러 핸들링  (1) 2021.05.23
6. This  (3) 2021.05.02
5. 변수와 데이터  (0) 2021.04.25
4. 함수  (0) 2021.04.18