프론트엔드 개발자의 기록 공간

[JavaScript DeepDive] 13~15장 본문

모던 자바스크립트 Deep Dive

[JavaScript DeepDive] 13~15장

[리우] 2022. 2. 10. 17:34

📖 학습 목차

  • 13장_스코프(scope)
  • 14장_전역 변수의 문제점
  • 15장_let, const 키워드와 블록 레벨 스코프

 

✅ 스코프(Scope)

Scope(유효범위)는 자바스크립트를 포함한 모든 프로그래밍 언어의 기본적이며 중요한 개념이다.

유효범위라고도 부르며 변수가 어느 범위까지 참조되는지를 뜻 한다.

간단하게 정리한 개념은 여기서 확인할 수 있다.

 

var var1 = 1; // 코드의 가장 바깥 영역에서 선언한 변수

if (true) {
  var var2 = 2; // 코드 블록 내에서 선언한 변수
  if (true) {
    var var3 = 3; // 중첩된 코드 블록 내에서 선언한 변수
  }
}

function foo() {
  var var4 = 4; // 함수 내에서 선언한 변수

  function bar() {
    var var5 = 5; // 중첩된 함수 내에서 선언한 변수
  }
}

console.log(var1); // 1
console.log(var2); // 2
console.log(var3); // 3
console.log(var4); // ReferenceError: var4 is not defined
console.log(var5); // ReferenceError: var5 is not defined

변수는 자신이 선언된 위치에 의해 자신이 유효한 범위, 즉 다른 코드가 변수 자신을 참조할 수 있는 범위가 결정된다.

모든 식별자(변수 이름, 함수 이름, 클래스 이름 등)은 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효범위가 결정된다. 이를 스코프라 한다. 즉, 스코프는 식별자가 유효한 범위를 말한다.

 

자바스크립트 엔진은 코드를 실행할 때 코드의 문맥을 고려한다. 코드가 어디서 실행되며 주변에 어떤 코드가 있는지에 따라 결과를 만들어 낸다.

코드의 문맥과 환경
"코드가 어디서 실행되며 주변에 어떤 코드가 있는지"를 렉시컬 환경이라고 부른다.
즉, 코드의 문맥은 렉시컬 환경으로 이뤄진다. 이를 구현한 것이 "실행 컨텍스트"이며,
모든 코드는 실행 컨텍스트에서 평가되고 실행된다.

 

✍ 스코프 체인

함수는 중첩될 수 있으므로 함수의 지역 스코프도 중첩될 수 있다.

이는 스코프가 함수의 중첩에 의해 계층적 구조를 갖는다.

 

위의 코드를 통해 스코프 체인을 알아보자

// 전역 스코프 코드의 가장 바깥 영역에서 선언한 변수
var1 = 1;
var2 = 2; 
var3 = 3;

  // 전역 - foo의 지역 스코프
  var4 = 4;
  
    // 전역 - foo - bar의 지역 스코프
    var5 = 5;

위의 코드 처럼 스코프가 계층적으로 연결된 것을 스코프 체인이라고 한다.

변수를 참조할 때 자바스크립트 엔진은 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 선언된 변수를 검색 한다.

 

✅ 전역 변수의 문제점

14장은 간략하게 넘어 간다.

지역 변수의 생명 주기는 함수의 생명 주기와 일치한다.

 

var 키워드로 선언한 전역 변수의 생명 주기는 전역 객체의 생명 주기와 일치한다.

전역 변수의 생명 주기

전역 변수는 생명 주기가 길고, (메모리를 계속 차지)

전역 변수의 검색 속도가 가장 느리다. (스코프 체인내에서 제일 최상위에 있기 때문)

따라서 즉시 실행 함수네임스페이스 객체, 모듈 패턴, ES6 모듈 사용으로 전역 변수의 사용을 억제해야 한다.

// 즉시 실행 함수
(function () {
  var foo = 10; // 즉시 실행 함수의 지역 변수
  // ...
}());

console.log(foo); // ReferenceError: foo is not defined
// 네임스페이스 객체
// 사실 네임스페이스 객체 자체가 전역 변수에 할당되므로 그다지 유용하진 않다.

var MYAPP = {}; // 전역 네임스페이스 객체
MYAPP.name = 'Lee';

MYAPP.person = {
  name: 'Lee',
  address: 'Seoul'
};

console.log(MYAPP.name); // Lee
console.log(MYAPP.person.name); // Lee
// 모듈 패턴 : 변수와함수를 모아 즉시 실행 함수로 감싸 하나의 모듈로 만든다.
// 캡슐화가 된다는 장점이 있고 이는 클로저 기반으로 동작한다.

var Counter = (function () {
  // private 변수
  var num = 0;

  // 외부로 공개할 데이터나 메서드를 프로퍼티로 추가한 객체를 반환한다.
  return {
    increase() {
      return ++num;
    },
    decrease() {
      return --num;
    },
    getNum() {
      return num;
    }
  };
}());

// private 변수는 외부로 노출되지 않는다.
console.log(Counter.num); // undefined
console.log(Counter.getNum());   // 0
console.log(Counter.increase()); // 1
console.log(Counter.getNum());   // 1
console.log(Counter.increase()); // 2
console.log(Counter.getNum());   // 2
console.log(Counter.decrease()); // 1
console.log(Counter.decrease()); // 0

클로저의 간단한 개념은 여기서 확인할 수 있다.

 

✅ let, const 키워드와 블록 레벨 스코프

ES5까지 변수를 선언할 수 있는 유일한 방법은 var 키워드를 사용하는 것이었다.

하지만 var 키워드는 변수 중복 선언 허용, 함수 레벨 스코프, 변수 호이스팅으로 인해

예기치 못한 문제를 발생할 수 있다.

이를 해결하기 위해 let, const 사용을 적극 권장한다.

간단하게 호이스팅의 차이에 대해서만 언급하고 나머지는 생략하겠다. 

let, const 키워드와 블록 레벨 스코프 간단한 개념 여기서 확인할 수 있다.

(이전에 학습할 당시 정리해둔 포스팅이 있어서 자꾸 언급하게 되네...)

 

✍ 변수 호이스팅

var 키워드로 선언한 변수는 호이스팅으로 인해 변수 선언 시점 이전에서

참조를 할 수 있다.

하지만 let 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는것처럼 동작한다.

 

var 키워드로 선언한 변수는 런타임 이전에 자바스크립트 엔진에 의해 암묵적으로 "선언 단계"와 "초기화 단계"가 한번에 진행된다.

 

let 키워드로 선언한 변수는 "선언 단계"와 " "초기화 단계"가 분리되어 진행된다. 즉, 런타임이전에 자바스크립트 엔진에 의해 암묵적으로 선언 단계가 먼저 실행되지만 초기화 단계는 변수 선언문에 도달했을 때 실행된다.

만약 초기화 단계 이전에 변수에 접근하려고 하면 참조 에러가 발생한다. 스코프의 시작 지점(변수 선언) 지점부터 초기화 단계까지 변수를 참조할 수 없는 구간을 일시적 사각지대(TDZ) 라고 부른다.

 

const 키워드는 변수 선언과 동시에 할당을 한 번에 해야하는 것과 값을 변경할 수 없다는 조건(상수) 외에는 동일하다.

 

자바스크립트는 모든 선언(var, let, const, function, class 등)을 호이스팅한다. 단, ES6에서 도입된 let, const, class를 사용한 선언문은 호이스팅이 발생하지 않는 것처럼 동작한다.

 

✍ var vs let vs const

  • ES6를 사용한다면 var 키워드는 사용하지 않는다.
  • 재할당이 필요한 경우에 한정해 let 키워드를 사용한다. 이때 변수의 스코프는 최대한 좁게 만든다.
  • 변경이 발생하지 않고 읽기 전용으로 사용하는 원시 값과 객체에는 const 키워드를 사용한다.

변수를 선언하는 시점에는 재할당이 필요할지 잘 모르는 경우가 많다.

일단 변수를 선언할 때는 const 키워드를 사용하고, 반드시 재할당이 필요하다면

그때 let 키워드로 변경해도 결코 늦지 않는다.

 

👨‍💻 이번 장은 어쩌면 당연한 내용이지만, 제일 기초적인 내용이다. 쉽다고 넘어가기보다는

다시 한번 복습한다는 개념으로 읽어보는 것을 추천한다.

728x90
Comments