JavaScript_24.Scope 그리고 switch문

🙋🏻‍♀️·2022년 4월 30일
0

wecode

목록 보기
17/40

24-1. Scope

scope란 '변수가 어디까지 쓰일 수 있는지'의 범위를 의미한다.
is not defined 라는 에러메세지를 본 적이 있는가? 변수가 아직 선언되지 않았다는 뜻이다.

나는 분명 변수를 선언했는데????!! 하겠지만 변수를 선언한 영역(block에) 접근할 수가 없어서 컴퓨터가 변수가 선언 되었다는 사실을 알지 못해서 그랬을것이다.

그래서 변수가 선언되지 않았다는 오류메세지를 출력하게 되는 것이다. 어떤 변수는 여기저기서 쓸 수 있는 반면에, 어떤 변수는 특정 함수 내에서만 쓸 수 있었다.

이러한 개념이 바로 scope이다.



24-2. Block

scope를 더 파헤치기 전에 먼저 알아야할 개념은 block이다.
그 동안에 functionif문,for문을 하면서 이미 block을 많이 경험했다.
block이란 중괄호({}, curly brace)로 감싸진 것을 말한다.


📌function의 내부는 하나의 block이다.

function hi() {
 return 'i am block';
}
console.log(hi());
//i am block'



📌for문도 하나의 block이다.

for (let i=0; i<10; i++) {
 count++;
}



📌if문의 {}도 하나의 block이다.

if (i === 1) {
 let j = 'one';
 console.log(j);
}



⚠️주의

{}(block)내부에서 변수가 정의되면 변수는 오로지 {}(block)내부에서만 사용할 수 있다.
{}(block)내부에서 정의된 변수는 local(지역) 변수라고 부른다.

아래의 코드에는 문제가 있다. 무엇이 문제인지 찾아보자.

function getResult() {
  let result = 10;
 
  return result;
}

// 자바스크립트 에러! 
// getResult 내부의 scope에 접근할 수 없다
console.log(result);

console.log(result)에서 getResult 내부에 접근이 불가능하기 때문에 result라는 변수의 존재를 알지 못한다. result라는 변수는 getResult 함수의 {}(block)에서만 사용할 수 있다.
🔹console.log(result); 부분을 주석처리하고 getResult() 입력





24-3. Global(전역) Scope

scope는 변수가 선언되고 사용할 수 있는 공간이다. scope 외부(block밖)에서는 특정 scope의 변수에 접근할 수가 없다. ⭐block밖인 global scope에서 만든 변수를 global variable(전역변수)라고 한다. 코드 어디서든 접근이 가능해서 변수값을 확인할 수 있다.

const color = 'red';
console.log(color);

function returnColor() {
 console.log(color);
 return color;
}

console.log(returnColor());

returnColor함수 내에서, returnColor함수 밖에 있는 color라는 변수를 return 해주었다. color라는 변수는 global 변수이기 때문에 returnColor함수의 block에서도 접근이 가능해서 'red'를 반환한것.⭐




23-4. Scope의 오염

global 변수를 쓰면 여기 저기서 접근하기 쉬워서 좋다고 생각할 수 있지만, 너무 남용하면 프로그램에 문제를 일으킬 수 있다. global 변수를 선언하면, 해당프로그램의 어디에서나 사용할 수 있는 global namespace를 갖는다. namespace라는 것은 변수 이름을 사용할 수 있는 범위라는 뜻이다. scope라고도 하고 특히 변수이름을 얘기할 때는 namespace라고도 한다.

global 변수는 프로그램이 종료될때까지 계속 살아있다. 이 말은 local 변수는 {}-block이 끝나면 더이상 변수가 살아있지 않고 쓸 수 없다는 말이다.

global 변수가 계속 살아있어서 변수값이 계속 변한다면 해당 변수를 트래킹하기도 어렵고 이 변수는 어디에서 왜 필요한지 알려면 도대체 어디서 let,const로 선언을 했는지 찾아 나서야 한다.



🔹아래는 scope이 오염된 대표적인 예이다.

const satellite = 'The Moon';
const galaxy = 'The Milky Way';
let stars = 'North Star';

const callMyNightSky = () => {
  stars = 'Sirius';
 
  return 'Night Sky: ' + satellite + ', ' + stars + ', ' + galaxy;
};

console.log(callMyNightSky());
console.log(stars);

//'Night Sky: The Moon, Sirius, The Milky Way'
'Sirius'

stars이라는 global 변수가 있다.
callMyNightSky함수에서 새로운 변수를 선언하고 싶었는데 깜빡하고 let키워드를 작성하지 않았다.
callMyNightSky을 호출하면 stars 변수에 "sirius"이 할당된다.
• global 변수였던 stars에 영향이 갔다!
• 다른 함수에서 global 변수인 stars을 사용하고 싶은데 값이 수정된 "sirius"로 사용하게 된다.

❗⭐앞으로 함수가 몇 십개 있을지도 모르는 상황에서 저렇게 global 변수를 남용하다가는 어디서 어떻게 값이 수정될지 알 수 없다!⭐❗





24-5. 좋은 Scopping 습관

위와 같이 global 변수가 여기저기서 수정되면 안되기 때문에 변수들은 block scope로 최대한 나눠놔야 한다.

• 타이트한 scope(tightly scoping)의 변수는 코드 품질을 올려준다!
• 코드가 block 으로 명확하게 구분되기 때문에 코드 가독성이 올라간다.
• 코드가 한줄 한줄 쭉 나열된 것이 아니라 각각의 기능별로 block을 나누면 코드가 이해하기 쉬워진다.
• 나중에 코드를 수정할 일이 있을 때, 코드를 오랜만에 보더라도 잘 나뉘어 있어서 유지보수가 쉬워진다.
• 프로그램이 끝날때까지 변수가 살아있는 것이 아니라서(block이 끝나면 local 변수의 삶이 끝나서) 메모리 절약도 된다.


⭐⭐즉, 한마디로 요약하면 global 변수는 쓰지 않도록 노력해야 하고, 최대한 {}내에서 let,const을 사용하여 변수를 새로 만들어서 쓰자는 말이다.




이번에는 if문의 {}의 block scope를 보자.

function logSkyColor() {
 const dusk = true;
 let myColor = 'blue';

 if (dusk) {
  let myColor = 'pink';
  console.log(myColor);// pink
}

console.log(myColor); // blue
}
console.log(myColor); // 에러!!

if문에 {}의 코드 block을 사용하였다. 여기에 myColor라는 변수를 let으로 선언하였다.
if문 내에서는 "pink"라고 할당하였고, if문 밖에서는 "blue"라고 할당한 것에 주목하기!!

사실 이 코드는 function의 block,scope,if문의 block,scope로, 모두 block을 잘 사용하였는데도 scope namespace가 오염된 것이다. ⭐왜냐면 같은 이름의 변수를 사용했기 때문이다

이렇게 새로운 block에서 변수를 쓸 때는 항상 다른 이름으로 변수를 선언해야한다는 것을 잊지 말자.





✔️switch문

• 스위치 표현식은 한 번 평가된다.
• 표현식의 값은 각 경우의 값과 비교된다.
• 일치하는 항목이 있으면 연결된 코드 블록이 실행된다.
• 일치하는 항목이 없으면 기본 코드 블록이 실행된다.

switch(expression) {
  case x:
    // code block
    break;
  case y:
    // code block
    break;
  default:
    // code block
}

📌예시 1.

이 getDay()메서드는 0에서 6 사이의 숫자로 요일을 반환합니다.

(일요일=0, 월요일=1, 화요일=2 ..)

이 예에서는 요일 번호를 사용하여 요일 이름을 계산합니다.

switch (new Date().getDay()) {
  case 0:
    day = "Sunday";
    break;
  case 1:
    day = "Monday";
    break;
  case 2:
     day = "Tuesday";
    break;
  case 3:
    day = "Wednesday";
    break;
  case 4:
    day = "Thursday";
    break;
  case 5:
    day = "Friday";
    break;
  case 6:
    day = "Saturday";
}
//Saturday



📌예시 2.

이 getDay()메서드는 0에서 6 사이의 숫자로 요일을 반환합니다.
오늘이 토요일(6)도 일요일(0)도 아닌 경우 기본 메시지를 작성합니다.

switch (new Date().getDay()) {
  case 6:
    text = "Today is Saturday";
    break;
  case 0:
    text = "Today is Sunday";
    break;
  default:
    text = "Looking forward to the Weekend";
}
// Today is Saturday

케이스가 스위치 블록의 default 마지막 케이스일 필요는 없습니다.

switch (new Date().getDay()) {
  default:
    text = "Looking forward to the Weekend";
    break;
  case 6:
    text = "Today is Saturday";
    break;
  case 0:
    text = "Today is Sunday";
}
// "Today is Saturday"

📌break문이 없는 경우의 예시를 알아보자.

let a = 2 + 2;

switch (a) {
  case 3:
    alert( '비교하려는 값보다 작습니다.' );
  case 4:
    alert( '비교하려는 값과 일치합니다.' );
  case 5:
    alert( '비교하려는 값보다 큽니다.' );
  default:
    alert( "어떤 값인지 파악이 되지 않습니다." );
}
alert( '비교하려는 값과 일치합니다.' );
alert( '비교하려는 값보다 큽니다.' );
alert( "어떤 값인지 파악이 되지 않습니다." );
//case 4, case 5, default 실행



📌공통 코드 블록

때로는 다른 스위치 케이스가 동일한 코드를 사용할 것이다. 이 예제의 경우 4와 5는 동일한 코드 블록을 공유하고 0과 6은 다른 코드 블록을 공유한다.

switch (new Date().getDay()) {
  case 4:
  case 5:
    text = "Soon it is Weekend";
    break;
  case 0:
  case 6:
    text = "It is Weekend";
    break;
  default:
    text = "Looking forward to the Weekend";
}
//"It is Weekend";



📌switch문 세부 사항

• 여러 케이스가 케이스 값과 일치하는 경우 첫 번째 케이스가 선택된다.
• 일치하는 케이스가 없으면 프로그램은 default 레이블을 계속 사용한다.
default 레이블이 없으면 프로그램은 switch 뒤의 명령문으로 계속 진행한다.
• 엄격한 비교(===)를 사용한다.
• 값은 일치하는 동일한 유형(type)이어야 한다.
• 엄격한 비교는 피연산자가 같은 유형인 경우에만 참이 될 수 있다.



📌자료형의 중요성

let arg = prompt("값을 입력해주세요.");
switch (arg) {
  case '0':
  case '1':
    alert( '0이나 1을 입력하셨습니다.' );
    break;

  case '2':
    alert( '2를 입력하셨습니다.' );
    break;

  case 3:
    alert( '이 코드는 절대 실행되지 않습니다!' );
    break;
  default:
    alert( '알 수 없는 값을 입력하셨습니다.' );
}

0이나 1을 입력한 경우엔 첫 번째 alert문이 실행됩니다.
2를 입력한 경우엔 두 번째 alert문이 실행됩니다.
3을 입력하였더라도 세 번째 alert문은 실행되지 않습니다. 앞서 배운 바와 같이 prompt 함수는 사용자가 입력 필드에 기재한 값을 문자열로 변환해 반환하기 때문에 숫자 3을 입력하더라도 prompt 함수는 문자열 '3'을 반환합니다. 그런데 세 번째 case문에선 사용자가 입력한 값과 숫자형 3을 비교하므로, 형 자체가 다르기 때문에 case 3 아래의 코드는 절대 실행되지 않습니다. 대신 default문이 실행됩니다.





✍️Assignment

case 오른쪽에 설명해야 할 단어를 텍스트로 작성해놨습니다. description 변수에 해당 단어에 대한 설명을 딱 한 문장으로 작성해주세요.

 function whatIs(type) {
  let description;

  switch (type) {
    case 'scope':
      description = '변수가 쓰일 수 있는 범위';
      break;
    case 'block':
      description = '중괄호로 묶여진 단위';
      break;
    case 'global scope':
      description = '전역범위';
       break;
    case 'global variable':
      description = '전역변수: 전역에서 접근할 수 있는 변수';
       break;
    case 'block scope':
      description = '중괄호 안의 범위';
       break;
    case 'local variable':
      description = '지역변수: 중괄호 안에서만 쓸 수 있는 변수';
      break;
    case 'global namespace':
      description = '전역변수를 사용할 수 있는 범위';
      break;
    case 'scope pollution':
      description = '전역변수를 남용하여 값이 수정된 경우';
      break;
    default :
      description = ''
      break;
  }
 
  return description;
}

0개의 댓글