본문 바로가기
자바스크립트/바닐라 JS

[자바스크립트] TIL2 : 변수 선언과 할당 및 초기화 방법 / 변수 영역(스코프)의 정의

by charlie-lyc 2021. 4. 9.
728x90
반응형

▣ 변수(Variables)

자료(Data)가 저장된 메모리에는 나름의 주소가 있고, 이것은 일반적으로 이해하기 어려운, 굳이 알 필요 없는 숫자들의 집합일 것입니다. 그저 USB 또는 SSD에서 램으로, 다시 CPU로, 소위 말해 컴퓨터의 기억장치와 연산장치 사이에서 자료들이 이동하면서 연산이 이루어진다는 사실 정도만 알고 있어도 상관없습니다.

다만 코딩을 통해 하고자 하는 일은 원하는 프로그램을 만들기 위해 좀 더 적극적으로 컴퓨터의 자원을 이용하고 자료들을 제어하는 것이므로 메모리의 주소까지는 몰라도, 자료를 알아볼 수 있는 어떤 곳에 담아 두고 어떻게 활용할 것인가 정도는 제어할 수 있어야 합니다. 바로 이럴 때 필요한 것이 '변수(Variable)'입니다.

'지금부터 이 이름을 변수로 사용함'을 컴퓨터에게 알려주는 것을 '선언'이라 하고, 선언된 변수에 값을 주는 것을 '할당', 그리고 이 둘을 동시에 하는 것을 '초기화'라고 합니다.

 


□ 변수 이름

/* Variable */
var name1 = 'John';
var Name = 'John';
var _name = 'John';
var $name = 'John';
var 1name = 'John'; // Uncaught SyntaxError: Invalid or unexpected token

/* Multi words variable */

// Camel case
var firstName = 'Pete';

// Pascal case
var FirstName = 'Jerry';

// Snake case
var first_name = 'Ethan';

var firstname = 'Jack'; // Not recommended

 

자바스크립트에서 변수 이름은 오직 문자(영어 대소문자), 숫자 _ (Underscore), $ 기호만 사용할 수 있으며, 단 변수 이름의 시작은 숫자가 아니어야 합니다. 변수 이름을 숫자로 시작할 경우 위와 같이 에러가 발생됩니다. 그리고 여러 단어를 혼합한 형태의 변수 이름은 위의 코드에서 볼수 있듯이 세 가지 유형(캐멀, 파스칼, 스네이크)으로 만들어 사용하며, 소문자로만 구성된 변수명은 권장하지 않습니다. 세 가지 중에서도 일반적으로 캐멀형을 가장 많이 사용합니다.

 


 var, let, const

/* var */
var name2;
console.log(name2); // undefined

name2 = 'John';
console.log(name2);

var name2 = 'Pete';
console.log(name2);

/* let */
let otherName;
console.log(otherName); // undefined

otherName = 'Jerry';
console.log(otherName);

let otherFullName = 'Jerry Maguire';
console.log(otherFullName);

let otherName = 'Ethan'; // Uncaught SyntaxError: Identifier 'anotherName' has already been declared

/* const */
const anotherName; // Uncaught SyntaxError: Missing initializer in const declaration

const anotherName = 'Ethan';
console.log(anotherName);

anotherName = 'Jack'; // Uncaught TypeError: Assignment to constant variable

const anotherName = 'Jack'; // Uncaught SyntaxError: Identifier 'otherName' has already been declared

 

위의 코드와 같이 자바스크립트에서는 변수를 선언하기 위해서 미리 약속된 예약어를 사용하는데, 세 가지(var, let, const)를 사용할 수 있습니다. 아래의 결과를 통해 살펴보면 var는 선언, (재)할당, 초기화재선언이 가능하고,  let선언, (재)할당, 초기화는 가능하지만 재 선언할 경우에 에러를 발생시킵니다. 그리고 const는  초기화만 가능하고 선언, (재)할당, 재 선언 모두 에러를 발생시킵니다. 참고로 'undefined'는 '변수 사용을 선언만 한 상태'이지 아직 어떤 값인지 정의되지 않았다는 의미입니다.

 

 

처음에는 var 만 사용되었으나 코드가 길어질수록 재선언으로 인한 혼동이 야기되어 const와 let이 추가되었습니다. 즉, const 나 let으로 변수를 선언하는 것이 예측 가능하고 더 안전합니다. 다소 복잡한 내용 같지만 변수 선언 시 사용법은 간단합니다. 변수를 처음 사용할 때 "undefined"의 위협으로부터 벗어날 수 있는 ① const를 이용해 초기화하고, 코딩을 하다가 재할당이 필요한 경우에 const를 ② let으로 고친 후 값을 재할당함으로써 변수를 계속 사용할 수 있습니다.

var를 사용하지 않는 또 하나의 중요한 이유는 '스코프(Scope)'이며 이에 대한 내용은 아래에서 다루도록 하겠습니다.    

 


 객체 배열의 초기화

/* Initialize object literals */
const person = {
  name: "John",
  age: 30,
};
console.log(person);

person.name = "Pete";
person['age'] = 32;
console.log(person);

/* Initialize array literals */
const numbers = [1, 2, 3, 4, 5];
console.log(numbers);

numbers[5] = 6;
console.log(numbers);

numbers.push(7);
console.log(numbers);

 

문자나 숫자 같은 값뿐만 아니라 객체( { }로 둘러싸인 데이터 )나 배열( [ ]로 둘러싸인 데이터 ) 또한 변수를 이용하여 초기화할 수 있습니다. 위의 코드에서 객체 person은 const로 초기화되었지만, 그 부분적인 특성(property)인 name과 age는  '.' 이나 '[ ]' 를 통해 접근하므로 그 값을 얼마든지 재할당할 수 있습니다. 배열 numbers 또한 인덱스나 메서드를 통해 배열 내의 구성 요소에 접근하여 그 값을 변경할 수 있습니다.

 

 


반응형

 

스코프(Scope)

스코프는 새로운 변수가 선언되어 사용될 때 '변수가 영향을 미치는 범위' 정도로 파악할 수 있습니다. 자세한 설명은 링크들을 참조하기 바라며, 다소 어렵게 느껴질 수 있는 부분이므로 완전한 이해보다는 아래의 코드를 천천히 살펴보면서 '이런 개념이구나'하고 넘어가면 좋을 것 같습니다.

 


 전역, 함수, 블럭 스코프

/* Global Scope */
var a = 1;
let b = 2;
const c = 3;

/* Function Scope */
const test = function () {
    var a = 4;
    let b = 5;
    const c = 6;
    console.log('Function Scope :', a, b, c);
}
test();

/* Block Scope */
if (true) {
  var a = 7;
  let b = 8;
  const c = 9;
  console.log('Block Scope    :', a, b, c);
}

console.log('Global Scope   :', a, b, c);

 

위의 코드에서 보듯이 세 가지 전역(Global) 스코프, 함수(Function) 스코프, 블럭(Block) 스코프로 나누어지며, 각각의 스코프에서 모두 var, let, const와 변수 a, b, c 를 이용하여 서로 다른 값을 할당한 후 실행한 결과, 아래와 같이 스코프 별 값들을 출력하였습니다..

 

 

위의 결과를 통해 파악할 수 있는 것은 const와 let으로 초기화되었을 경우 함수, 블럭, 그리고 전역의 스코프 어디에서나 변수 모두 각자의  범위 또는 { } 안에 갇혀 있다는 것입니다. 그런데 var의 경우에는 마지막 92번 라인의 실행 결과 ' 1, 2, 3 ' 이 아니라 ' 7, 2, 3 ' 인 걸로 보아 86번 라인 If 구문의 블럭 안에서 초기화된 변수 a전역 변수 a영향을 미치게 된 것입니다.

외부에 전역 스코프가 있고 그 안에 함수 스코프와  블럭 스코프가 있으므로, 만일 함수 또는 블럭 스코프 내에서 전역 변수와 동일한 변수에 대한 초기화가 없다면 외부의 변수가 내부 스코프에 영향을 줄 수는 있습니다. 하지만 내부 변수가 외부 스코프에까지 영향을 미치는 것은 코딩에서 무척 혼동스러운 일입니다. 

 


 For문의 블럭 스코프

/**
 * For Loop에서 Block Scope : let과 var의 경우를 번갈아 실행해 보자.
 */
var i = 0;

/* let */
for (let i = 0; i < 3; i++) {
  console.log(i);
}

/* var */
for (var i = 0; i < 3; i++) {
  console.log(i);
}

console.log("i :", i);

 

대표적인 반복문(Loop statement)인 for문블럭을 한 번 더 예로 들어 보면 혼동스럽다는 것을 좀 더 이해할 수 있을 것 같습니다. 위의 코드에서 let을 이용하는 경우와 var를 이용하는 경우를 번갈아 실행한 결과는 아래와 같습니다.

 

 

위의 결과에서 보듯이 109번 라인의 i의 값이 97번 라인에서 초기화한 0인 걸로 보아, 100번 라인 let i = 0; 의 스코프는 명확하게 for 블럭 안에 갇혀 있습니다. 하지만 아래의 결과를 보면 105번 라인의 var i = 0;for 블럭을 벗어나 전역 변수에 영향을 미치는 것을 알 수 있습니다. 이러한 이유가 바로 var를 사용하지 않고, const와 let을 사용하는 결정적인 이유입니다.

 

 


 

728x90
반응형

댓글