스코프(Scope)

Samuel .J·2021년 12월 29일
0
post-thumbnail

시작 하기에 앞서...

Scope는 무엇일까요? 원래 영어 단어 자체는 범위라는 의미를 가집니다. 자바스크립트에서 이야기하는 스코프 역시, 무언가 제한된 범위를 잘 들여다보기 위해 사용되는 개념이라고 추측해볼 수 있을거예요.

컴퓨터 공학, 그리고 자바스크립트에서의 스코프도 "범위"의 의미를 가지고 있습니다. 더 자세하게는 "변수의 유효범위"로 사용됩니다. 이번 유닛을 진행하면서 Scope의 종류와 각 선언 키워드 (let, const)를 어떻게 사용하고 무엇을 권장하는지 알아봅시다.

오늘 배울것

  • 스코프의 의미와 적용 범위를 이해할 수 있다
  • 스코프의 주요 규칙을 이해할 수 있다
  • 중첩 규칙
    • block scope와 function scope
    • 전역 스코프와 지역 스코프
    • 전역 변수와 지역 변수간의 우선 순위
    • let, const, var 의 차이
    • 전역 객체(window)의 이해

예제로 알아보는 스코프(1)

  • 콘솔에 순서대로 출력되는 결과는 무엇인가요?
let username = 'kimcoding';
if (username) {
  let message = `Hello, ${username}!`;
  console.log(message); // "Hello, kimcoding!"
}
console.log(message); // ReferenceError
  • 해설

    4번째 줄에서 message를 출력할 때는, 3번째 줄의 username을 바깥 스코프에서 가져왔으므로 정상적으로 출력됩니다.
    그러나, 6번째 줄에서는 message라는 변수 자체가 안쪽 스코프에 선언되어 있으므로, 바깥쪽에서는 접근할 수 없습니다.

  • 콘솔에 순서대로 출력되는 결과는 무엇인가요?

let greeting = 'Hello';
function greetSomeone() {
  let firstName = 'Josh';
  return greeting + ' ' + firstName;
}

console.log(greetSomeone()); // 'Hello Josh'
console.log(firstName); // ReferenceError
  • 해설

    greeting 변수는 바깥 스코프에 정의되어 있으므로, 함수 안쪽에서 사용할 수 있습니다. 따라서 greeting 변수와 firstName 변수의 조합의 의해 'Hello Josh' 문자열이 출력됩니다.
    반면에, firstName 변수는 안쪽 스코프에 정의되어 있으므로 바깥쪽에서는 접근이 불가능합니다. 따라서 ReferenceError를 냅니다.

스코프와 주요 규칙

첫번째 규칙 : 안쪽 스코프에서 바깥쪽 스코프로는 접근 가능
하지만 바깥쪽 스코프에서 안쪽 스코프로는 불가능

  • 위 예시처럼 변수에 접근할 수 있는 범위가 존재합니다. 중괄호(블록) 안쪽에 변수가 선언되었는가, 바깥쪽에 변수가 선언되었는가가 중요합니다. 이 범위를 우리는 스코프라고 부릅니다.

  • 범위가 중괄호(블록) 또는 함수에 의해 나뉘어지고, 그 범위를 스코프라고 부른다고 했습니다.
    여기서 우리가 알 수 있는 규칙은 바로 다음과 같습니다.
  • 바깥쪽 스코프에서 선언한 변수는 안쪽 스코프에서 사용 가능합니다.
    반면에, 안쪽에서 선언한 변수는 바깥쪽 스코프에서는 사용할 수 없습니다.

두번째 규칙 : 스코프는 중첩이 가능하다.

  • 특별히 가장 바깥쪽의 스코프는 전역 스코프(Global Scope)라고 부른다.
  • 전역의 반대말은 지역(local)으로 전역이 아닌 다른 스코프는 전부 지역 스코프(local scope)라 한다.
  • 지역 스코프에서 선언한 변수는 지역 변수,
    전역 스코프에서 선언한 변수는 전역 변수라 한다.
  • 스코프 규칙에서는 지역 변수는 전역 변수보다 더 높은 우선순위를 갖는다.
let name = '김코딩'; // 전역 변수

function showName() {
  let name = '박해커'; // 지역 변수
}

예제로 알아보는 스코프(2)

  • 순서대로 콘솔에 출력되는 결과는?
let name = '김코딩';

function showName() {
  let name = '박해커'; // 지역 변수
  console.log(name); // 두번째 출력 : 박해커
}

console.log(name); // 첫번째 출력 : 김코딩
showName();
console.log(name); // 세번째 출력 : 김코딩

해설

  • 첫번째 출력은 첫째 줄에서 전역 변수로 선언된 name을 져온다
  • showName함수 안쪽에 선언된 지역 변수 name은 애초에 스코프 규칙에 의해 접근할 수 없기 때문에 따라서 "김코딩"을 출력
  • 두번째 출력은 함수 안에 선언한 name이라는 지역 변수에 접근을 하고있다.
  • 변수 이름이 전역 변수와 같지만, 지역 변수가 전역 변수보다 우선순위가 높으므로, 지역변수 name이 출력이 된다.
  • 동일한 변수 이름으로 인해 바깥쪽 변수가 안쪽 변수에 의해 가려지는(shadow) 이러한 현상을 쉐도잉(variable shadowing)이라고 부른다.
  • 그러므로 두번째 출력은 "박해커" 이다.
  • 세번째 출력은 첫번째 출력과 마찬가지로 전역 변수 name을 출력한다.
  • 지역 변수에 선언된 name 변수는 안쪽 스코프이므로 접근이 불가능
  • 따라서 "김코딩"을 출력한다.
  • 순서대로 콘솔에 출력되는 결과는?
let name = '김코딩';

function showName() {
  name = '박해커';
  console.log(name); // 두번째 출력 : 박해커
}

console.log(name); // 첫번째 출력 : 김코딩
showName();
console.log(name); // 세번째 출력 : 박해커

해설

  • 앞서 문제와는 다르게, 세번째 줄에서 let키워드를 사용한 선언이 존재하지 않는다.
  • 이는, '박해커'라는 값으로 할당하고 있는 name변수는 전역에 선언된 name 변수를 그대로 사용하겠다는 의미
  • 지역 스코프에서 새로 선언되지 않으면 그냥 같은 변수이다.
  • 따라서 showName 함수가 실행되기 전, 처음에 '김코딩'을 출력하고
  • 그 이후에는 전역변수 name의 값이 바뀌기 때문에 두번째 및 세번째 출력에 '박해커'가 출력된다.

위 내용 정리...

  1. 스코프의 정의를 다시 한 번 살펴 보면 스코프는 "변수 접근 규칙에 따른 유효 범위"입니다.
  2. 안쪽 스코프에서 바깥쪽 스코프로는 접근할 수 있지만 반대는 불가능합니다.
  3. 스코프는 중첩이 가능하다.
  4. 스코프에는 스코프 위치에 따라 지역 변수, 전역 변수가 있다.

스코프의 종류와 let, const, var

스코프의 종류

    1. 블록 스코프(block scopre) : 중괄호를 기준으로 범위가 구분된다.
      if (true) {
        console.log('i am in the block')
      }
      for (let i = 0; i < 10; i++) {
        console.log(i); // 변수 i는 중괄호 안에서만 사용 가능
      }
      {
        console.log('it works')
      }
    1. 함수 스코프(function scope) : function 키워드가 등장하는 함수 선언식 및 함수 표현식은 함수 스코프를 만든다.
      function getName(user) {
        return user.name;
      }
      let getAge = function (user) {
        return user.age;
      }
  • 유의할점

    • 화살표 함수는 블록 스코프로 취급된다. 함수 스코프가 아니다.
      let getAge = user => {
        return user.age;
      } 
      // 같은 함수여도, 화살표 함수를 사용하면 블록 스코프 취급됨
      //
      let getAge = function (user) {
        return user.age;
      }
      // function 키워드를 사용하면 함수 스코프다.

블럭 스코프(Block scope)와 var 키워드

for (let i=0; i<10; i++) {
console.log(i);
}
console.log('final i :', i);
//블록 스코프 안에서 정의된 변수 i는 블록 범위를 벗어나는 즉시 접근할 수 없다.
//결과로는 ReferenceError가 나오게 됨.
--------------------------------------------------------
for(var i =0; i<5; i++) {
console.log(i);
}
console.log('final i:',i);
//앞서 보았던 코드와 동일하나, 변수 선언을 let 대신 var를 이용했다.
//이에 대한 결과는 5이다. 왜 그럴까?
//결론부터 말하면, var 키워드는 for 문이 만들어낸 블록 스코프를 무시한다.

var 키워드와 let 키워드

var 키워드
  • var 키워드로 정의한 변수는 블록 스코프를 무시하고, 함수 스코프만 따른다.
  • 그러나, 모든 블록 스코프를 무시하는 건 아님. 화살표 함수의 블록 스코프는 무시하지 않는다.
  • 함수 스코프는 함수의 실행부터 종료까지이고, var 선언은 함수 스코프의 최상단에 선언된다.
  • 선언 키워드 없는 선언은 최고 스코프에 선언된다.
  • 함수 내에서 선언 키워드 없는 선언은, 함수의 실행 전까지 선언되지 않 은 것으로 취급한다.
  • 보통 코드를 작성할 때 블록은 들여쓰기가 적용되고, 그 구분이 시각적으 로 분명하다.
  • 따라서 많은 사람들은 블록 스코프를 기준으로 코드를 작성하고, 생각하기 마련이다.
  • 그러나 var는 이 규칙을 무시하므로, 코드를 작성하는 사람이 블록 스코 프/함수 스코프에 대한 이해가 없으면 코드가 다소 혼란스러울 수 있다.
  • 블록 단위로 스코프를 구분했을 때 훨씬 더 예측 가능한 코드를 작성할 수 있으므로 let 키워드의 사용을 권장한다.
  • var을 사용하지 않는다 해도, 함수 스코프는 let으로 선언된 변수의 접 근 범위를 제한한다.
var 키워드 측징
  • var키워드 보다 let키워드가 더 안전하다.
  • 왜냐하면 var 키워드는 재선언을 해도 아무런 에러도 내지 않지만, let 키워드는 재선언을 방지한다.

const 키워드

  • 변하지 않는 값, 곧 상수(constant)를 정의할 때에 사용
  • let 키워드와 동일하게, 블록 스코프를 따른다.
  • 값의 변경을 최소화하여 보다 안전한 프로그램을 만들 수 있다.
  • 값을 새롭게 할당할 일이 없다면, const 키워드의 사용이 권장된다.
  • const는 값의 재할당이 불가능하다.
  • 값을 재할당할 경우 TypeError를 내므로,
    의도하지 않은 값의 변경을 막을 수 있다.

let, var, const 키워드 비교

letconstvar
유효 범위블록 스코프 및 함수 스코프블록 스코프 및 함수 스코프함수 스코프
값 재할당가능불가능가능
재선언불가능불가능가능

변수 선언에서 주의할 점

window 객체(브라우저 only)

  • window객체는 사실 브라우저의 창(window)을 의미하는 객체이지만, 이와 별개로 전역 영역을 담고 있기도 한다.
  • 함수 선언식으로 함수를 선언하거나, var로 전역 변수를 만들면, window 객체에서도 동일한 값을 찾을 수가 있다.

전역 변수는 최소화

  • 전역 변수는 가장 바깥 스코프에 정의한 변수이다.
    따라서, 어디서든 접근이 가능하다.
  • 보통 애플리케이션을 만들 때에는, 내가 직접 작성하지 않은 수많은 다른 함수와 로직이 포함된다.
  • 너도나도 똑같은 이름으로 전역 변수를 선언하려고 한다면 분명 문제가 발생할 것이다.
  • 이를 side effect라고 한다.
  • 전역 변수를 최소화하는 것은 side effect를 줄이는 좋은 방법이다.

let, const를 주로 사용하라

  • var 키워드는 블록 스코프를 무시한다.
  • 또한 재선언을 해도 에러를 내지 않는다.
  • 따라서, let과 const를 주로 사용해라....
  • 전역 변수를 var로 선언하는 것은 브라우저의 내장 기능을 사용하지 못하게 만들 수도 있다.

선언 없는 변수 할당 금지

  • 선언 없이 변수를 할당하지 마라..

  • 선언 없이 변수를 할당하면, 변수는 var로 선언한 전역 변수처럼 취급이 된다.

    function showAge(){
    age = 90;
    //여기서 age는 전역 변수 취급을 받는다.
    console.log(age);
    }
    showAge();
    console.log(age); //90
    console.log(window.age); //90
  • age를 선언한 적이 없으나, 값을 할당하면서 마치 var로 선언된 전역 변수처럼 작동한다.

Strict Mode

  • 실수를 방지하기 위해 Strict Mode를 사용할 수 있다.
  • 선언 없는 변수 할당의 경우도 Strict Mode는 에러로 판단한다.
  • Strict Mode를 적용하려면, js 파일 상단에 'use strict' 라고 입력하면 됩니다. (따옴표 포함)
profile
기록하는 코린이의 블로그🥸

0개의 댓글