[JS] #1 let, const 변수

simoniful·2021년 4월 26일
0

ES6 JS - Basic

목록 보기
1/18
post-thumbnail

변수 구분

  • 로컬(지역) 변수, 글로벌(전역) 변수로 구분할 수 있습니다.
  • 변수를 구분하는 이유는 변수의 기능과 목적이 다르기 때문입니다.
  • 글로벌 변수의 기능 및 목적은 다른 JS파일에서 변수값을 공유하고 파일에서 공통 변수 개념으로 사용할 수 있다는 것입니다. 이는 좋아보이고 사용하는데는 편리할 수 있지만 처리 속도가 떨어집니다.
  • 로컬 변수의 기능 및 목적으로 빠르게 식별자를 해결하기 위해 가까운 스코프의 변수를 사용하려는 것입니다.

글로벌 변수 오해

// "use strict"
value = 100;
function point() {
  value = 300;
  console.log("함수: ", value);
};
point();
// 300
  • 글로벌 변수는 글로벌 오브젝트의 로컬 변수입니다. var value = 100처럼 var 키워드 사용이 정상입니다.

  • 그런데 여기서 var 키워드를 작성하지 않으면 글로벌 변수로 간주하는데 이것이 문제가 될 수 있습니다.

    • var 키워드를 사용하지 않고 value를 글로벌 변수에 선언 후 100을 할당한 코드입니다. point 함수에서 value에 300을 할당 후 출력을 합니다.
    • 이 때 value는 로컬 변수가 아니므로 글로벌 오브젝트의 value 변수에 300을 할당하게 됩니다. 이처럼 함수 안에서 글로벌 변수에 값을 설정하는 것은 좋은 목적이 아닙니다.
    • 설계를 할 때 로컬 변수와 글로벌 변수를 구분한 목적을 고려해야 합니다

use strict 사용

"use strict"
function point() {
  try {
    value = 300;
    console.log("함수: ", value);
  } catch(e) {
    console.log("글로벌 변수 사용 불가");
  }
};
point();
// 300
  • ES5에서 var 키워드를 사용하지 않을 경우 에러가 발생하도록 'use strict'모드를 추가했습니다. 하지만, 근본적인 접근방법은 아닙니다.
  • ES6+에서는 "use strict"가 기본환경입니다. (전체는 아닙니다.)

블록 스코프

let sports = "축구";
sports = "농구";
console.log(sports);
// let sports = "배구";  --- syntax error 
{
  let sports = "탁구";
  console.log(sports);
};
// 농구
// 탁구
  • 블록 기준으로 값이 정의되며 블록 안과 밖의 변수는 구분됩니다. 즉 같은 이름이지만 각각 다르게 존재할 수 있습니다.

    • 중괄호 { 코드 }
    • function name() { 코드 }
    • if(a === 1) { 코드 }
  • 블록 안과 밖의 스코프가 다르기에 변수 이름이 같아도 값이 대체되지 않습니다.

    • 블록 안에서 블록 밖의 변수는 접근 가능
    • 블록 밖에서 블록 안의 변수는 접근 불가능
  • 스코프에 같은 이름을 사용하는것을 불가능합니다.

    • 동일 스코프에 같은 이름 let 변수 선언 사용 불가

function 블록

let sports = "축구";
function show() {
  let sports = "농구"
  console.log("안:",sports);
};
show();
console.log("밖:",sports);
// 안: 농구
// 밖: 축구
  • function name(){ }도 블록 스코프입니다.
  • 스코프가 다르기 때문에 function 안과 밖에 같은 이름의 let변수 선언이 가능합니다.
let sports = "축구";
function show() {
  console.log(sports);
};
show();
  • function 밖의 let 변수를 function 안에서 사용 가능합니다 (클로저)

    try-catch 문

    let sports = "축구";
    try {
     let sports = "농구";
     console.log("안:", sports);
     abc = error;
    } catch(e) {
     console.log("catch: ", sports);
    };
    console.log("밖: ",sports);
    // 안: 농구
    // catch: 축구
    // 밖: 축구 
  • try-catch문도 블록 스코프 이며 try블록 { }기준으로 안과 밖에 같은 이름의 let변수 선언이 가능합니다.

  • catch()에서 try밖의 변수를 사용 가능합니다.

switch-case 문

let item = 1;
switch (item) {
  case 1:
    let sports;
    break;
  case 2:
    // let sports; // 주석 해제시 컴파일 에러 발생
  default:
    console.log(sports);
};
  • switch 문도 블록 스코프입니다.
  • switch 블록 기준으로 같은 이름의 let 변수 작성이 불가능합니다.
  • 이때 case, default는 자체적으로 다른 블록을 가지지 않습니다.

let 변수

개요

let sports = "축구";
if(sports) {
  let sports = "농구";
  console.log("안:", sports);
};
console.log("밖: ", sports);
// 안: 농구
// 밖: 축구
  • let book = "책"
    블록(block) 스코프를 가진 변수
    변수가 선언된 블록이 스코프
  • 스코프 적용 기준
    블록{ }, 문, 표현식
  • 블록 { }안과 밖이 스코프가 다릅니다.
    변수 이름이 같아도 값이 대체되지 않습니다

let변수 선언

let value1;
let value2 = "테스트";
let value3 = "테스트2", value4;
let value3 = "테스트2", value4 = "테스트3";
let value1 = (10 + 20);
  • Syntax
    let name1 [= value1] [, name2 [= value2] ]

  • name1, name2에 변수 이름 작성

    • 식별자로 사용합니다.
    • [ ] 는 생략 가능을 나타냅니다.
    • 값을 할당하지 않아도 됩니다.
let book = "책";
let one = 1, two = (10 + 20);
// let five = 5, let six = 6;
// let five = 5, var six = 6;
  • value1, value2에 초기값 작성
    표현식 작성이 가능하고 평가 결과 사용도 가능합니다.
    let은 처음에 한 번만 작성합니다.
    let과 var를 콤마로 구분하여 같이 사용할 수 없습니다.

let 변수와 var 변수 차이

for()문에서 반복할 때마다 var 변수는 스코프를 갖지 않는 반면, let 변수는 스코프를 가집니다.

var 변수와 스코프

<ul class="sports">
	<li>축구</li>
	<li>농구</li>
	<li>야구</li>
</ul>
<input id="output" type="text" readonly>
var node = document.querySelector(".sports");
var $output = document.querySelector("#output");
for(var k = 0; k< node.children.length; k++) {
  node.children[k].onclick = function(event) {
    event.target.style.backgroundColor = "yellow";
    $output.value = k;
  };
};

어떤 것을 클릭해도 3이 출력되는 것을 확인할 수 있습니다. 이는 var k = 0; 에서 k변수의 스코프는 함수 전체이기 때문에 세가지 li 요소들이 하나의 k변수를 가지게되고 for문이 끝날때 할당된 3을 출력하게 되는 것입니다.

let 변수와 스코프

<ul class="sports">
	<li>축구</li>
	<li>농구</li>
	<li>야구</li>
</ul>
<input id="output" type="text" readonly>
var node = document.querySelector(".sports");
var $output = document.querySelector("#output");
for(let k = 0; k< node.children.length; k++) {
  node.children[k].onclick = function(event) {
  event.target.style.backgroundColor = "yellow";
    $output.value = k;
  };
};

var k =0; 을 let k =0; 으로 바꾸었습니다. 이제 이벤트 발생시 그 때의 k값을 출력합니다. 이를 통해 k는 블록단위로 가져가며 구분되는것을 확인할 수 있습니다.

이는 장점만 있는것은 아닌데, 만일 반복문의 반복 횟수가 수천 수만번이 넘어가게 될 때 let변수로 설정하면 각각의 블록이 메모리를 차지하게도면서 퍼포먼스가 떨어질 수 있습니다.


let 변수와 this

var music = "음악";
let sports = "축구";
console.log(this.music, this.sports);
// 음악
// undefined
  • 글로벌 오브젝트에서 let변수를 this로 참조하는것은 불가능합니다.
    ⇒ var music = "음악"은 window 오브젝트에 설정됩니다.
    ⇒ let sports = "축구";는 window 오브젝트에 설정되지않습니다.
    ⇒ this.music은 글로벌 오브젝트 window에 설정되어 출력이 되지만, sports는 window에 설정되지 않았기에 undefined가 출력됩니다.

글로벌 오브젝트에서 var / let 변수가 설정되는 위치 구조

"use strict"
var globalVar = "글로벌";
let globalLet = "블록";
console.log(this.globalVar);
console.log(this.globalLet);
// 글로벌
// undefined

  • globalVar는 window객체의 globalVar에 등록되는것을 확인할 수 있습니다.
  • globalLet는 window 객체에서 찾을 수 없고 Script 라는 영역에 저장된 것을 확인할 수 있습니다. 이는 엔진이 블록을 만들고 이를 스코프로 사용하여 설정하는 개념입니다.
  • 그렇기에 this.globalVar는 window객체에서 globalVar 프로퍼티를 찾을 수 있기에 '글로벌'이 출력되지만, globalLet은 글로벌 오브젝트에 등록되지 않았기에 undefined가 출력됩니다.

다수의 js파일 사용

모든 js파일에서 글로벌 오브젝트에 작성한 var 변수와 let변수를 공유할 수 있습니다.

  • 단, 블록 안에 작성한 것은 공유하지 않습니다.

var globalVar = "var변수";
var globalLet = "let변수":
console.log(globalVar);
console.log(globalLet);

위처럼 두 개의 js파일을 사용하면서 first.js에서 선언한 변수들을 second.js에서 사용이 가능합니다.

그리고, 글로벌 오브젝트에 작성했지만 공유하고 싶지가 않을 경우 블록을 사용하면 글로벌 오브젝트의 지역 변수로 사용할 수 있습니다.

블록의 형태

  • Block
  • Local
  • Script
  • Global

정리

var globalBal = "var 변수";
let globalLet = "let 변수";
{
  let globalBlock = "block 변수";
};

글로벌 오브젝트에 작성하면

  • var 변수: window에 설정되고 공유됩니다.
  • let 변수: Script에 설정되고 공유됩니다. (다만 설정되는 영역이 Script입니다.)
    • window.sports = { }처럼 의도적으로 작성하지 않아도 됩니다.
  • { let 변수 }: Block에 설정, 공유하지 않습니다.
    • 글로벌 오브젝트에서만 사용하는 로컬 변수로 사용합니다.
function showLocal() {
  var localVar = "var 변수";
  let localLet = "let 변수";
  {
    let blockLet = "block 변수";
  };
};

함수에 작성

  • var 변수, let변수: Local
  • 함수 내 { let 변수 }: Block

변수의 접근 권한으로 밖에서는 안의 변수를 사용하기 어렵지만, 안에서는 밖의 변수를 사용할 수 있는 점에서 스코프의 개념을 이해하는 것이 필요합니다.


호이스팅

console.log("music 변수:", music);
var music = "음악";
// music 변수: undefined
  • ES5의 실행 콘텍스트 처리 순서
  1. 함수 선언문 설정
  2. 변수 이름을 바인딩
    • 변수값은 undefined
  3. 소스 코드 실행
    • 출력 코드(console.log)아래에 var music = "음악"이 있습니다.
    • 이렇게 변수가 아래에 위치하지만 식별자 해결을 할 수 있습니다. 하지만 이 때 music의 값은 undefined입니다.
    • 이것을 호이스팅이라 하는데 식별자 해결을 하지 못하면 에러가 발생합니다.
try {
  console.log("music 변수:", music);
} catch(e) {
  console.log("에러가 발생했습니다");
}
  let music = "음악";
// 에러가 발생했습니다
  • let 변수는 호이스팅(Hoisting)되지 않음
    ⇒ let 변수 앞에서 변수 사용이 불가능, 인식 불가

let 변수를 인식하는 시점

// 변수가 모두 아래에 작성되어 있습니다.
console.log(globalVar);
var globalVar = "var 변수";
// undefined
  1. console.log(globalVar);
    • console.log에 undefined가 출력됩니다.
    • 글로벌 오브젝트 Global(window)를 살펴보면 globalVar 변수값이 undefined이지만 선언되 있습니다.
  2. var globalBVar = "var 변수";
    • 이 부분에서 undefined였던 globalVar의 초기값(undefined)이 'var 변수' 로 할당됩니다.
try {
  console.log(globalLet);
} catch(e) {
  console.log("globalLet 인식 불가");
}
let globalLet;
console.log(globalLet);
// globalLet 인식 불가
// undefined
  1. try-catch문
    아래의 globalLet을 인식하지 못하기에 에러가 발생합니다. (globalLet 인식 불가 출력)
  2. let globalLet;
    • 이때 Script 영역에 globalLet이 표시됩니다. 즉, 변수 선언을 실행해야 표시됩니다.
    • 값을 할당하지 않고 변수를 선언만 하면 엔진이 undefined를 할당합니다.
  3. console.log(globalLet);
    • let 변수는 변수 선언을 실행한 후에 변수를 인식할 수 있습니다.
    • 즉, 식별자를 해결할수 있어서 undefined를 출력합니다.

block안에 let 변수 작성

{
  console.log(variable);
  var variable = "var 변수";
  let blockLet = "let 변수";
}
// undefined
  • 글로벌 오브젝트(Global(window))를 살펴보면 variable의 변수값이 undefined이지만 등록되어있습니다.
  • blockLet 변수도 undefined로 표시됩니다. 하지만, 호이스팅을 사용할 수는 없습니다.
  • 앞에서 글로벌 변수는 Script에 변수가 표시되지 않습니다. 하지만, block은 이 때 표시가 됩니다. 즉, 엔진이 블록 내부를 엔진이 한 번 훑었다는 의미가 됩니다.
  • let 변수가 별도의 영역(block)에 설정되는 개념을 MDN에서는 "temporal dead zone"으로 기술합니다.
    → ES6 스펙에 작성된 용어는 아닙니다.
  • temporal 에서 let변수가 undefined인 상태를 나타내는 뉘앙스가 풍기며, dead zone에서 let 변수에 값을 할당한 후에는 임시 상태가 해제되어 변수를 사용할 수 있다는 뉘앙스를 풍깁니다.
    → let blockLet = "let 변수"; 이 실행된 후에는 변수를 사용할 수 있다는 의미가 됩니다.

정리

  1. 초기화 단계(코드를 실행하기 전)에서 정적 환경의 선언적 환경 레코드에 변수 이름을 바인딩합니다.

    • var 변수는 undefined를 초기값으로 설정하고
    • let 변수는 초기값을 설정하지 않습니다.

    엔진에서는 이런 처리를 초기화자(Initializer)로 구분하고 있습니다.

  2. 변수 이름으로 식별자를 해결할 때

    • 변수에 값이 있으면 변수로 인식하고
    • 변수에 값이 없으면(temporal 상태) 변수로 인식하지 않는 개념입니다.
  3. let 변수 선언을 실행하면 그때 값이 설정되며

    • 값을 할당하지 않고 선언만 하면 엔진이 undefined를 할당합니다.
    • 하지만, let변수에서 undefined는 표시를 위한 것이지 엔진 관점에서는 값을 가지고 있지 않은 상태입니다.
      그렇기 때문에 호이스팅이 불가능합니다.
  4. 따라서, 변수 선언을 실행한 후에는 변수가 값을 갖고 있으므로 변수를 인식할 수 있습니다.

  5. 엔진에서는 초기화자(Initializer)와 Binding List 메커니즘을 사용합니다.


const 변수

변경 불가능한 변수 선언

구문: name1 [=value1], name2 [=value2]]

  • name1에 변수 이름 작성, 식별자로 사용

    const sports = "축구";
    try {
      sports = "농구";
    } catch(e) {
      console.log("const 할당 불가");
    };
    // const 할당 불가
  • value1, value2에 초기값 작성

    • 반드시 값을 작성해야 하며 변수 선언만 할 수 없습니다.
    • 표현식 작성 사용가능하며 평가 결과를 사용합니다.
  • JS에서 상수는 대문자 사용이 관례입니다.

    const bonus = 100;
    const POINT = 200;
  • 우선 let이 아닌 const사용을 검토해야 합니다.

  • const 변수 전체를 바꿀 수는 없지만 Object의 프로퍼티 값을 바꿀 수는 있습니다.

    const book = {title: "책"};
    try {
      book = {title: "음악책"};
    }catch(e) {
      console.log("const 전체 할당 불가");
    }
    book.title = "미술책";
    console.log(book.title);
    // const 전체 할당 불가
    // 미술책

    ⇒ book에 값을 할당하면 const이기 때문에 에러가 발생합니다. 하지만, book내부의 title 프로퍼티 값은 변경할 수 있습니다.

  • const 변수 전체를 바꿀 수는 없지만 배열의 엘리먼트 값을 바꿀 수는 있습니다.

    const book = ["책"];
    try {
      book = ["음악책"];
    } catch(e) {
      console.log("const 전체 할당 불가");
    }
    book[0] = "미술책";
    console.log(book[0]);
    // const 전체 할당 불가
    // 미술책

    ⇒ book에 값을 할당하면 const이기 때문에 에러가 발생합니다. 하지만 book 배열의 엘리먼트 값은 바꿀 수 는 있습니다.

profile
소신있게 정진합니다.

0개의 댓글