0%

201117_TIL(변수_1)

오늘 한 것

개념 및 용어 정리

변수

  • 만약 숫자 ‘10’을 메모리에 저장한다면 숫자 ‘10’에 접근할 때마다 복잡한 메모리 주소를 찾아 직접 접근해야한다. 하지만 메모리 주소에 직접 접근하는 것은 매우 위험하다. 잘 못 접근하게 되면 미지의 메모리를 변경할 수도 있기 때문이다.
  • 위와 같은 이유로 프로그래밍 언어는 값을 메모리에 저장하고, 저장된 값을 읽어 들여 재사용하기 위해 변수라는 메커니즘을 제공한다.
  • 변수는 값의 위치를 나타내는 상징적인 이름이며 식별자라고도 부른다.
1
2
3
4
5
// 변수는 하나의 값을 저장하기 위한 수단이다.
var nyong = 10;

// 연산을 통해 생성된 값을 변수를 통해 메모리 공간에 저장할 수도 있다.
var result = 10 + 20;
  • 변수에 값을 저장하는 것을 할당(assignment), 변수에 저장된 값을 읽어 들이는 것을 참조(reference)라 한다.
  • 안에 담긴 값의 의미를 반영하여 지은 변수 이름은 저장된 값의 의미를 명확히 할 수 있어 가독성을 높여준다.
  • 코드는 컴퓨터에게 내리는 명령이면서 동시에 개발자를 위한 문서이기도 하다. 명확한 네이밍은 코드를 이해하기 쉽게 만들며 타인이 내 코드를 쉽게 해석할 수 있게 돕는다.

변수 선언

  • 변수를 사용하려면 반드시 선언이 필요하다. 변수를 선언할 때는 var, let, const 키워드를 사용한다.
1
2
var score; // 변수 선언
console.log(score); //undefined
  • 위와 같이 변수를 선언한 후 출력하면 undefined 값이 출력된다. 이는 현재 변수 안에 메모리 공간은 확보됐지만 어떤 값도 할당하지 않은 것이다.
  • 자바스크립트에서 변수를 선언하면 다음의 절차를 거친다
    1. 선언 단계 : 변수 이름을 등록해서 자바스크립트 엔진에 변수의 존재를 알린다.
    2. 초기화 단계 : 값을 저장하기 위한 메모리 공간을 확보하고 암묵적으로 undefined를 할당해 초기화한다.
  • 자바스크립트에서는 초기화 값으로 정의되지 않았다는 뜻인 undefined를 할당한다. 이것도 할당이라면 할당이지만 의도하지 않았다면 제대로된 값을 할당한 행위는 아니다.

호이스팅

1
2
3
console.log(score); // undefined

var score; // 변수 선언문
  • 자바스크립트는 인터프리터 언어인 것을 저번에 설명하였다. 그렇다면 console.log(score)구문을 실행하면 var score가 실행되기 전이기 때문에 변수 참조 에러(ReferenceError)가 발생해야하지만 undefined값이 출력됐다.
  • 그 이유는 변수 선언이 소스코드가 한 줄씩 순차적으로 실행되는 런타임(runtime)이 아니라 이전 단계에서 먼저 실행되기 때문이다.
  • 자바스크립트 엔진은 런 타임에 진입하기 전 준비과정으로 모든 선언문(변수,함수 선언문 등)을 찾아 먼저 실행한다. 그렇기 때문에 var로 선언된 변수인 score가 먼저 실행되어 위의 현상이 나타나는 것이다.
  • 이처럼 변수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 변수 호이스팅(variable hoisting)이라 한다.
  • var, let, const, function, function*, class 키워드를 사용해서 선언하는 모든 식별자(변수, 함수, 클래스 등)는 호이스팅된다.

값 할당

1
var score = 80; // 변수 선언과 값의 할당
  • 변수 선언은 소스코드가 순차적으로 실행되는 시점인 런타임 이전에 먼저 실행되지만 값의 할당은 소스코드가 순차적으로 실행되는 시점인 런타임에 실행된다. 아래 코드를 보자.
1
2
3
4
5
6
console.log(score); // undefined

var score; // ① 변수 선언
score = 80; // ② 값의 할당

console.log(score); // 80
  • 참고로 변수에 값을 할당할 때는 이전 값 undefined가 저장되어 있던 메모리 공간을 지우고 그 메모리 공간에 할당 값 80을 새롭게 저장하는 것이 아니라 새로운 메모리 공간을 확보하고 그 곳에 할당 값을 저장한다.(즉, 메모리 주소가 동일하지 않다.)
  • 그 이유는 undefined값도 할당이라면 할당이라고 위에서 말했는데 변수에 값을 제대로 할당할 경우(사실은 처음이 아닌 ‘재할당’이다.) 새로운 메모리 공간을 확보하고 그 메모리 공간에 새로운 값을 저장하기 때문이다.
  • 그렇다면 전에 값이 저장된 메모리 공간이 불필요하게 존재한다고 걱정할 수 있는데 이는 가비지 콜렉터라는 기능에 의해 자동 삭제된다. 단, 언제 삭제될지는 모른다.

변수 네이밍 규칙

  1. 식별자는 특수문자를 제외한 문자, 숫자, 언더스코어(_), 달러 기호($)를 포함할 수 있다.
  2. 단, 식별자는 특수문자를 제외한 문자, 언더스코어(_), 달러 기호($)로 시작해야 한다. 숫자로 시작하는 것은 허용하지 않는다.
  3. 예약어는 식별자로 사용할 수 없다.(예약어는 프로그래밍 언어에서 사용되고 있거나 사용될 예정인 단어를 말한다.)
  4. 영어 이외의 문자로도 명명할 수 있지만 권장하지 않는다.
  5. 자바스크립트는 대소문자를 구별한다.
  6. 주석이 필요없어도 의미를 알 수 있는 이름으로 짓자.
  7. 네이밍 컨벤션을 활용하자.
1
2
3
4
5
6
7
8
9
10
11
12
13
// 카멜 케이스 (camelCase)
var firstName;

// 스네이크 케이스 (snake_case)
var first_name;

// 파스칼 케이스 (PascalCase)
var FirstName;

// 헝가리언 케이스 (typeHungarianCase)
var strFirstName; // type + identifier
var $elem = document.getElementById("myId"); // DOM 노드
var observable$ = fromEvent(document, "click"); // RxJS 옵저버블

리터럴

  • 리터럴(literal)은 약속된 ‘기호를 사용해 값을 생성’하는 표기 방식(notaion)을 말한다.
  • 이해할 수 있는 문자(아라비아 숫자, 알파벳, 한글 등) 또는 미리 약속된 기호(‘’, “”, ., [], {}, // 등)로 표기한 코드다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 정수 리터럴
100
// 부동소수점 리터럴
10.5
// 2진수 리터럴(0b로 시작)
0b01000001
// 8진수 리터럴(ES6에서 도입. 0o로 시작)
0o101
// 16진수 리터럴(ES6에서 도입. 0x로 시작)
0x41

// 문자열 리터럴
'Hello'
"World"

// 불리언 리터럴
true
false

// null 리터럴
null

// undefined 리터럴
undefined

// 객체 리터럴
{ name: 'Lee', address: 'Seoul' }

// 배열 리터럴
[ 1, 2, 3 ]

// 함수 리터럴
function() {}

// 정규표현식 리터럴
/[A-Z]+/g

표현식

  • 표현식(expression)은 값으로 평가될 수 있는 문(statement)이다. 즉, 표현식이 평가되면 새로운 값을 생성하거나 기존 값을 참조한다.
  • 때문에 리터럴도 표현식이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 50 + 50은 리터럴과 연산자로 이뤄져 있다. 하지만 50 + 50도 평가되어 숫자 값 100을 생성하므로 표현식이다.
var score = 50 + 50;

// 변수 식별자를 참조하면 변수 값으로 평가된다.
score; // -> 100

// 다음과 같이 다양한 표현식이 있지만 값으로 평가된다는 점에서 모두 동일하다. 즉, 값으로 평가되는 문은 모두 표현식이다.
// 문법적으로 값이 위치할 수 있는 자리에는 표현식도 위치할 수 있다는 것을 의미한다. 표현식은 다른 표현식의 일부가 되어 새로운 값을 만들어낼 수 있다.
// 리터럴 표현식
10;
("Hello");

// 식별자 표현식(선언이 이미 존재한다고 가정)
sum;
person.name;
arr[1];

// 연산자 표현식
10 + 20;
sum = 10;
sum !== 10;

// 함수/메서드 호출 표현식(선언이 이미 존재한다고 가정)
square();
person.getName();

  • 문(statement)은 프로그램을 구성하는 기본 단위이자 최소 실행 단위이다. 문의 집합으로 이뤄진 것이 프로그램이며, 문을 작성하고 순서에 맞게 나열하는 것이 프로그래밍이다.

  • 문은 여러 토큰으로 구성된다. 토큰(token)이란 문법적인 의미를 가지며, 더 이상 나눌 수 없는 코드의 기본 요소다. 예를 들어, 키워드, 식별자, 연산자, 리터럴, 세미콜론(;)이나 마침표(.) 등의 특수 기호는 문법적인 의미를 가지며, 문법적으로 더 이상 나눌 수 없는 코드의 기본 요소이므로 모두 토큰이다.

  • 표현식은 문의 일부일 수도 있고 그 자체로 문이 될 수도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
// 변수 선언문은 값으로 평가될 수 없으므로 표현식이 아니다.
var x;

// 1, 2, 1 + 2, x = 1 + 2는 모두 표현식이다.
// x = 1 + 2는 표현식이면서 완전한 문이기도 하다.
x = 1 + 2;

// 표현식이 아닌 문은 값처럼 사용할 수 없다.
var foo = var x; // SyntaxError: Unexpected token var

// 표현식인 문은 값처럼 사용할 수 있다
var foo = x = 100;
  • 표현식인 문과 표현식이 아닌 문을 구별하는 가장 간단하고 명료한 방법은 변수에 할당해 보는 것이다. 표현식인 문은 값으로 평가되므로 변수에 할당할 수 있다.

데이터 타입

자바스크립트(ES6)는 7개의 데이터 타입을 제공한다.

  • 원시 타입(primitive type)

    • 숫자(number) 타입: 숫자. 정수와 실수 구분 없이 하나의 숫자 타입만 존재
    • 문자열(string) 타입: 문자열
    • 불리언(boolean) 타입: 논리적 참(true)과 거짓(false)
    • undefined 타입: var 키워드로 선언된 변수에 암묵적으로 할당되는 값
    • null 타입: 값이 없다는 것을 의도적으로 명시할 때 사용하는 값
  • 심벌(symbol) 타입: ES6에서 추가된 7번째 타입

    • 객체 타입 (object/reference type): 객체, 함수, 배열 등

숫자 타입

  • 자바스크립트는 하나의 숫자 타입만 존재한다.
  • ECMAScript 사양에 따르면 숫자 타입의 값은 모든 수를 실수로 처리하며, 정수만 표현하기 위한 데이터 타입(integer type)이 별도로 존재하지 않는다.
1
2
3
4
// 숫자 타입은 모두 실수로 처리된다.
console.log(1 === 1.0); // true
console.log(4 / 2); // 2
console.log(3 / 2); // 1.5
  • 2진수, 8진수, 16진수는 아래와 같이 표기한다.
1
2
3
4
5
6
7
var binary = 0b01000001; // 2진수
var octal = 0o101; // 8진수
var hex = 0x41; // 16진수

console.log(binary); // 65
console.log(octal); // 65
console.log(hex); // 65
  • 숫자 타입은 추가적으로 특별한 값들도 표현할 수 있다.
1
2
3
4
// 숫자 타입의 세 가지 특별한 값
console.log(10 / 0); // Infinity
console.log(10 / -0); // -Infinity
console.log(1 * "String"); // NaN <- 대소문자 구별 주의

문자열

  • 자바스크립트의 문자열은 원시 타입이며, 변경 불가능한 값(immutable value)이다. 때문에 문자열이 생성되면 그 문자열을 변경할 수 없다. C나 Java와는 문자열 타입이 다르다.
  • 자바스크립트에서 일반적인 표기법은 작은따옴표를 사용한다. 하지만 작은따옴표(‘’), 큰따옴표(“”), 백틱(``)으로도 가능하다.
  • 따옴표로 감싸지 않으면 키워드나 식별자로 인식한다.
1
2
3
4
5
6
7
8
9
10
11
// 문자열 타입
var string;
string = "문자열"; // 작은따옴표
string = "문자열"; // 큰따옴표
string = `문자열`; // 백틱 (ES6)

string = '작은따옴표로 감싼 문자열 내의 "큰따옴표"는 문자열로 인식된다.';
string = "큰따옴표로 감싼 문자열 내의 '작은따옴표'는 문자열로 인식된다.";

// 따옴표로 감싸지 않은 hello를 식별자로 인식한다.
var string = hello; // ReferenceError: hello is not defined

템플릿 리터럴

  • 템플릿 리터럴은 아래와 같이 문자열 처리 기능을 제공한다.

    • 멀티라인 문자열(multi-line string)
    • 표현식 삽입(expression interpolation)
    • 태그드 템플릿(tagged template)
  • 템플릿 리터럴은 백틱(``)으로 표현한다.

  1. 멀티라인 문자열

    • 템플릿 리터럴 내에서는 이스케이프 시퀀스를 사용하지 않고도 줄바꿈이 허용되며, 모든 공백도 있는 그대로 적용된다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var template = `<ul>
    <li><a href="#">Home</a></li>
    </ul>`;

    console.log(template);
    /*
    <ul>
    <li><a href="#">Home</a></li>
    </ul>
    */
  2. 표현식 삽입

    • 간단히 문자열을 삽입할 수 있다. 이를 통해 문자열 연산자보다 가독성 좋고 간편하게 문자열을 조합할 수 있다.
    1
    2
    3
    4
    5
    6
    7
    8
    var first = "Ung-mo";
    var last = "Lee";

    // ES6: 표현식 삽입
    console.log(`My name is ${first} ${last}.`); // My name is Ung-mo Lee.

    // 템플릿 리터럴이 아닌 일반 문자열에서의 표현식 삽입은 문자열로 취급된다.
    console.log("1 + 2 = ${1 + 2}"); // 1 + 2 = ${1 + 2}

undefined

  • undefined 타입의 값은 undefined가 유일하다.
  • var 키워드로 선언한 변수는 undefined로 초기화된다.
1
2
var foo;
console.log(foo); // undefined
  • 자바스크립트 엔진이 변수를 초기화할 때 쓰는 undefined를 개발자가 의도적으로 변수에 할당하는 것은 취지와 어긋나기 때문에 권장하지 않는다.
  • 변수에 값이 없다는 것을 명시하고 싶은 때는 undefined를 할당하는 것이 아니라 null을 할당한다.

null

  • null 타입의 값은 null이 유일하다. (대소문자 구별)
  • null은 변수에 값이 없다는 것을 의도적으로 명시할 때 사용하며 변수 값에 대한 참조를 제거한다.

symbol

  • 변경 불가능한 원시 타입의 값이다. 심벌 값은 절대 다른 값과 중복되지 않다.
  • 주로 이름이 충돌할 위험이 없는 객체의 유일한 프로퍼티 키를 만들기 위해 사용한다.
1
2
3
4
5
6
7
8
9
10
// 심벌 값 생성
var key = Symbol("key");
console.log(typeof key); // symbol

// 객체 생성
var obj = {};

// 이름이 충돌할 위험이 없는 유일무이한 값인 심벌을 프로퍼티 키로 사용한다.
obj[key] = "value";
console.log(obj[key]); // value

동적 타입 언어 / 정적 타입 언어

  • C나 Java의 경우 정적 타입(static/strong type) 언어이다. 변수를 선언할 때 데이터 타입을 명확하게 선언해야 한다.

  • 정적 타입 언어는 변수의 타입을 변경할 수 없고 선언한 타입에 맞는 값만 할당할 수 있다. 정적 타입 언어는 컴파일 시점에 타입 체크를 수행한다.

  • 자바스크립트는 동적 타입 언어로서 변수 타입을 선언하지 않고 var, let, const 키워드를 사용한다.

  • 자바스크립트의 변수는 데이터 타입의 값이라도 자유롭게 할당할 수 있다.

  • 자바스크립트 변수는 할당에 의해 타입이 결정된다. 또한 다른 타입 값을 재할당해 동적으로 타입을 바꿀 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var foo;
console.log(typeof foo); // undefined

foo = 3;
console.log(typeof foo); // number

foo = "Hello";
console.log(typeof foo); // string

foo = true;
console.log(typeof foo); // boolean

foo = null;
console.log(typeof foo); // object

foo = Symbol(); // 심벌
console.log(typeof foo); // symbol

foo = {}; // 객체
console.log(typeof foo); // object

foo = []; // 배열
console.log(typeof foo); // object

foo = function () {}; // 함수
console.log(typeof foo); // function
  • 변수의 타입이 동적으로 변하기 때문에 동적 타입 언어의 변수는 값의 변경에 의해 타입도 언제든지 변경될 수 있다. 따라서 변수 값을 확인하기 전에는 타입을 확신할 수 없다.

  • 자바스크립트 엔진에 의해 암묵적으로 타입이 자동으로 변환되는 경우도 있기 때문에 동적 타입 언어는 유연성(flexibility)은 높지만 신뢰성(reliability)은 떨어진다.

  • 이러한 동적 언어의 특징으로 아래와 같이 변수를 사용하길 권장한다.

    • 변수는 꼭 필요한 경우에 한해 제한적으로 사용한다. 필요한 만큼 최소한으로 유지해야한다.
    • 변수의 유효 범위(스코프)는 가능한 좁게 만들어 변수의 부작용을 억제해야 한다.
    • 전역 변수는 최대한 사용하지 않도록 한다.
    • 변수보다는 상수를 사용해 값의 변경을 억제한다.
    • 변수 이름은 변수의 목적이나 의미를 파악할 수 있도록 네이밍한다.

오늘 느낀 것

  • 용어 정리를 하면서 새롭게 배우는 것이 생각보다 많다. 사상누각되지 말자. 복습부터 새로 배우는 모든 것이 내 기본기가 된다.

Nyong’s GitHub