[es6] javascript 문법 (1) 변수

munju·2023년 3월 22일
1
post-thumbnail

이 포스트는 var,let,const차이,es6 문법정리 사이트를 참고하여 게시하였습니다. 많은 도움이 되었음에 작성자분들께 감사드립니다. :)

ECMAScript란?

-ECMA스크립트(ECMAScript, 또는 ES)는 자바스크립트를 표준화하기 위해 만들어진,
ECMA-262 기술 규격에 따라 정의하고 있는 표준화된 스크립트 프로그래밍 언어를 말한다.

자바스크립트에서 var로 변수 선언이 가능했는데, 왜 let과 const 가 나왔으며, 이 둘의 사용을 권장하는 것일까?
먼저 그동안 사용했었던 변수에 대해 알아보고 기초부터 개념정리를 한 후 넘어가도록 하겠다.

변수

변수(variable)는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인이름을 말한다. 말그대로 변할수 있는 값이다.

자바스크립트는 매니지드 언어(managed language)이기 때문에 개발자가 직접 메모리를 제어하지 못기때문에 개발자가 직접 메모리 주소를 통해 값을 저장하고 참조할 필요가 없고 변수를 통해 안전하게 값에 접근이 가능하다. 변수명(식별자)인 myNumber는 변수의 값이 아닌 메모리 주소를 기억하고 있다.
변수명을 사용하면, 자바스크립트 엔진이 변수명과 매핑된 메모리 주소를 통해 거기에 저장된 값(23)을 반환한다. 이처럼 변수에 값을 저장하는 것을 할당(assignment, 대입, 저장)이라 하며 변수에 저장된 값을 읽어 들이는 것을 참조(reference)라 한다. 그리고 변수명을 자바스크립트 엔진에 알리는 것을 선언(declaration)이라 한다.

-->지금껏 그냥 변수 지정해서 스크립트를 짜왔는데, 이와 같은 설명을 보고 나니 이해하기가 쉬웠다.

변수선언

자바스크립트에서 변수 선언은 선언 → 초기화 단계를 거쳐 수행된다.

  • 선언 단계: 변수명을 등록하여 자바스크립트 엔진에 변수의 존재를 알린다.
  • 초기화 단계: 값을 저장하기 위한 메모리 공간을 확보하고
    암묵적으로 undefined를 할당하고 초기화한다.
<script>
  var munju
  console.log(munju) 
  // output: undefined
</script>

var 키워드를 이용한 변수 선언은 선언 단계와 초기화 단계가 동시에 진행되어,
munju에 암묵적으로 undefined를 할당해 초기화한다. 반대로, console을 먼저 찍어도 반환 값이 undefined로 나온다.

<script> 
  console.log(mj) 
  // output: undefined
  var mj
</script>

이는 변수 선언이 런타임에서 되는 것이 아니라, 그 이전 단계에서 먼저 실행되기 때문이다.
자바스크립트 엔진은 소스코드를 한 줄씩 순차적으로 실행하기에 앞서, 변수 선언을 포함한 모든 선언문(ex. 변수 선언문, 함수 선언문 등)을 찾아내 먼저 실행한다.

즉, 변수 선언이 어디에 있든 상관없이 다른 코드보다 먼저 실행되는 특징을 호이스팅(hoisting)이라 한다. 변수 선언 뿐만 아니라, var, let, const, function, function*, class 키워드를 사용해 선언한 모든 식별자(변수, 함수, 클래스 등)는 호이스팅이 된다.
이내용은 아래 호이스팅에 대해 더 자세히 알아보도록 하겠다.

<script>

  var mj // 변수 선언 
  mj = 'mandu' // 값의 할당

  var mj = 'mandu' // 변수 선언과 할당새로운 변수에 새로운 값을 재할당할 수 있다
  console.log(mj) // output: mandu 

  var mj = fff
  console.log(mj) // output: fff
  
</script>

재할당은 변수에 저장된 값을 다른 값으로 변경하는 것으로, 만약 변경할 수 없는 값이라면
변수가 아니라 상수(constant)라 부른다.

변수 let / const

변수의 선언은 var, const, let 키워드로 할 수 있으며, ES6에서 const와 let이 추가되었다.
우선, var는 변수 선언방식에 있어 큰 단점을 가지고 있다.

<script>
  var name = 'munju'
  console.log(name) // munju

  var name = 'javascript'
  console.log(name) // javascript
</script>

변수를 한번 더 선언했음에도 불구하고 에러가 나오지 않고 각각 변수에 따라 다른 값이 콘솔창에 출력되었다. 유연한 변수 선언으로 간단한 테스트에는 편리할 수 있으나 코드량이 많아진다면 어디에서 어떻게 변수가 사용되었는지 파악하기가 어렵고 값이 바뀔 우려가 있다.

var 변수를 쓸 경우

  • 변수 중복 선언가능하나 예기치 못한 값을 반환할 수 있다.
  • 함수 레벨 스코프로 인해 함수 외부에서 선언한 변수는 모두 전역변수로 된다.
  • 변수 선언문 이전에 변수를 참조하면 언제나 undefined 를 반환한다.

es6에서 나온 let과 const는 위의 문제점을 해결해준다.

<script>
  let name = 'munju'
  console.log(name) // munju

  let name = 'javascript'
  console.log(name) 
  // Uncaught SyntaxError: Identifier 'name' has already been declared
</script>

name 이 이미 선언되었다는 에러메시지가 출력된다. (const로 바꿔서 넣어도 동일하게)

let과 const의 차이점

let

  • let은 변수에 재할당이 가능하다.
<script>
  let name = 'munju'

  name = 'velog'
  console.log(name) // output: velog
</script>

const

  • const는 반드시 선언과 초기화를 동시에 진행하여야한다.
<script>             
const name; // output: Uncaught SyntaxError: Missing initializer in const declaration
const name = 'mj'
</script>
  • const는 변수 재선언, 변수 재할당 모두 불가능하다.
    재할당의 경우, 원시 값은 불가능하지만, 객체는 가능하다. const 키워드는 재할당을 금지할 뿐, '불변'을 의미하지 않는다.
<script> 
    // 원시값의 재할당
    const name = 'mj'
    name = 'mj' 
    // output: Uncaught TypeError: Assignment to constant variable.

    // 객체의 재할당
    const name = {
      eng: 'mj',
    }
    name.eng = 'munju'

    console.log(name) // output: { eng: "munju" }
</script>

스코프

스코프(scope)는 식별자(변수명, 함수명, 클래스명등)의 유효범위를 뜻하며, 선언된 위치에 따라 유효범위가 달라진다. 전역에 선언된 변수는 전역 스코프를, 지역에 선언된 변수는 지역 스코프를 갖는다.

전역 변수는 어디에서든지 참조가 가능한 값이다. 반면, 지역 변수는 함수 몸체 내부라고 볼 수 있고, 지역 변수는 자신의 지역 스코프와 그 하위 지역 스코프에서 유효하다.

한 가지 주의해야 할 점은, 자바스크립트에서 모든 코드 블록(if, for, while, try/catch 등)이 지역 스코프를 만들며, 이러한 특성을 블록 레벨 스코프라 한다.

하지만 var 키워드로 선언된 변수는 오로지 함수의 코드 블록만을 지역 스코프로 인정한다.
이를 함수 레벨 스코프라 한다.

<script>
    var a = 1

    if (true) {
    var a = 5
    }

    console.log(a) // output: 5
</script>

함수가 아닌 곳에서 var 키워드를 이용해 a를 선언했기 때문에 전역 변수로 취급한다.
기존에 있던 a 변수가 중복 선언되면서, 최하단의 console에서도 출력 값이 5로 바뀐 것을 확인할 수 있다.

해당 예제는 코드가 짧아서 어디에서 문제가 일어난지 바로 알 수 있었지만, 실무에서는 그렇지 않다. 전역 변수로 인해 재할당이 발생하거나, 전역 스코프를 공유하기 때문에 어딘가에 동일한 이름이 있다면 예상치 못한 결과를 가져올 수 있는 위험이 있다. 따라서 오로지 함수 코드 블록만을 지역 스코프로 인정하는 var 대신, 블록 레벨 스코프를 지원하는 const와 let을 사용하는 것을 권장한다.

함수 호이스팅

호이스팅(Hoisting)이란, var 선언문이나 function 선언문 등을 해당 스코프의 선두로 옮긴 것처럼 동작하는 특성을 말한다. -> 위에서 변수 선언 뿐만 아니라, var, let, const, function, function*, class 키워드를 사용해 선언한 모든 식별자(변수, 함수, 클래스 등)는 호이스팅이 된다.라고 했었는데,

function 키워드로 선언한 모든 식별자도 호이스팅이 가능하다.

var 로 선언된 변수와는 달리 let 로 선언된 변수를 선언문 이전에 참조하면 참조 에러(ReferenceError)가 발생한다.

<script>
  console.log(abc); // undefined
  var abc;

  console.log(aaa); // Error: Uncaught ReferenceError: bar is not defined
  let aaa;
</script>

let으로 선언된 변수는 스코프의 시작에서 변수의 선언까지 일시적 사각지대에 빠지기 때문인데,
앞에서 말했듯이, 변수는 선언 > 초기화 > 할당 단계에 걸쳐 생성된다.

함수는 크게 네가지로 작성 할 수 있는데,

<script>
   //함수선언문 - 함수 이름 생략 불가
   function add(x,y) {
       return x + y
   }

   //함수 표현식 -함수 이름 생략가능
   var add = function(x,y) {
       return x+y
   }

    //함수이름으로 작성
    var add = function plus(x,y){
       return x+y
    }

    //Function 생성자 함수
    var add = new Function('x','y','return x+y')

    //화살표함수
    var add = (x,y) => x + y
</script>     
<script>
        //var
        // 스코프의 선두에서 선언 단계와 초기화 단계가 실행된다.
        // 따라서 변수 선언문 이전에 변수를 참조할 수 있다.

        console.log(foo); // undefined

        var foo;
        console.log(foo); // undefined

        foo = 1; // 할당문에서 할당 단계가 실행된다.
        console.log(foo); // 1
        
        let 으로 선언된 변수는 선언과 초기화 단계가 분리되어 진행된다.

        // 스코프의 선두에서 선언 단계가 실행된다.
        // 아직 변수가 초기화(메모리 공간 확보와 undefined로 초기화)되지 않았다.
        // 따라서 변수 선언문 이전에 변수를 참조할 수 없다.

        console.log(foo); // ReferenceError: foo is not defined

        let foo; // 변수 선언문에서 초기화 단계가 실행된다.
        console.log(foo); // undefined

        foo = 1; // 할당문에서 할당 단계가 실행된다.
        console.log(foo); // 1

        
        // 함수 참조
        console.dir(add) // output: f add(x, y)
        console.dir(sub) // output: undefined

        // 함수 호출
        console.log(add(2, 5)) // output: 7
        console.log(sub(2, 5)) // output: Uncaught TypeError: sub is not a function

        // 함수 선언문
        function add(x, y) {
        return x + y
        }

        // 함수 표현식
        var sub = function(x, y) {
        return x + y
        }
</script>

함수선언문의경우에는 함수 자체를 호이스팅 시킬 수 있다. 반면 함수 표현식은 변수 호이스팅과 같이 런타임 이전에 해당값을 undefined로 초기화만 시키고 런타임에서 해당 함수 표현식이 할당되어 객체가 된다.

  1. 변수 선언에는 기본적으로 const를 사용하고, 재할당이 필요한 경우에는 한정적으로 let을 사용하는것이 좋겠다.
  1. 재할당이 필요없는 상수와 객체에는 const사용 / ( 객체를 재할당 하는 경우는 흔하지 않다. const를 사용하면 의도치 않은 재할당을 방지해주기 때문에 보다 안전하겠다. )
profile
Web publisher

0개의 댓글