표현식과 문 & 데이터 타입 & 객체 & 불변성

오늘 공부한 내용

  • 표현식과 문

  • 데이터 타입

    • 원시 타입, 객체 타입
    • 데이터 타입의 필요성
    • 동적 타이핑
  • 원시 값과 객체의 비교

    • 변경 불가능한 값, 변경 가능한 값

1. 표현식과 문

값(value)은 식(표현식)이 평가되어 생성된 결과를 말한다. 평가란 식을 해석해서 값을 생성하거나 참조하는 것을 의미.

모든 값은 데이터 타입을 가지며, 메모리에 2진수로 저장된다. 메모리에 저장된 값은 데이터 타입에 따라 다르게 해석될 수 있다.

💬 ECMAScript 사양은 문자열과 숫자 타입 외의 데이터 타입의 크기를 명시적으로 규정하고 있지 않다. 따라서 이외의 데이터 타입은 자바스크립트 엔진 제조사의 구현에 따를 수 있다.

변수의 정의는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름이라고 했다. 변수에 할당되는 것은 값이다.

var sum = 10 + 20;

10 + 20이 평가된 결과인 30이 sum 변수에 할당된다. 값은 다양한 방법으로 생성할 수 있지만 가장 기본적인 방법은 리터럴을 사용하는 것.

리터럴

리터럴(literal)은 사람이 이해할 수 있는 문자 또는 약속된 기호를 사용해 값을 생성하는 표기법이다.

사람이 이해하는 문자: 숫자, 알파벳, 한글 등 / 미리 약속된 기호 - ‘’, “”, ., [], {} 등

자바스크립트 엔진은 런타임에 리터럴을 평가해 다양한 종류의 값을 생성한다.

[Literal (리터럴) - MDN Web Docs 용어 사전: 웹 용어 정의 MDN](https://developer.mozilla.org/ko/docs/Glossary/Literal)

표현식

표현식(expression)은 값으로 평가될 수 있는 문(statement)이다. 표현식이 평가되면 새로운 값을 생성하거나 값을 참조한다.

문(statement): 프로그램을 구성하는 기본 단위이자 최소 실행 단위

리터럴 혹은 연산의 결과를 평가하여 ‘값’을 생성한다. 변수에 할당된 것은 값이므로 변수 식별자를 참조하면 ‘값’으로 평가된다.

값으로 평가될 수 있는 문은 모두 표현식이다. 표현식은 값으로 평가된다고 했다. 표현식과 표현식이 평가된 값은 같다. 1 + 2 = 3에서 1 + 23과 같다. 표현식은 값처럼 사용할 수 있다.

var x = 1 + 2; // 3
x + 3; // 6

x가 숫자 값 3으로 평가된 값이므로 연산하여 새로운 값을 생성한다.

💬 값과 값으로 평가되는 표현식? 선언문도 표현식인가? 변수 선언하면 undefined이 출력되니까.

➡ 브라우저에서 프로그래머에게 변수 초기 상태를 알려주기 위해 출력되는 것. 선언문은 변수를 선언하고 초기화한다. 처음 undefined로 초기화될 텐데 선언문은 값을 반환하지 않는다.

변수선언문, 값으로 표현될 수 없는 문인 이유가 뭔가요?

완료 값

크롬 개발자 도구에서 표현식이 아닌 문을 실행하면 undefined를 출력한다. 이를 완료 값이라 한다. 완료 값은 표현식의 평과 결과가 아니므로 다른 값과 같이 변수에 할당할 수 없고 참조할 수도 없다.

문(statement)

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

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

💬 정의는 항상 어렵다. 형태소라고 생각하면 되려나?

문은 명령문이라고도 부른다. 컴퓨터에게 내리는 명령. 문은 선언문, 할당문, 조건문, 반복문 등으로 구분할 수 있다.

세미콜론과 세미콜론 자동 삽입 기능

세미콜론(;)은 문의 종료를 나타낸다. 자바스크립트 엔진은 세미콜론으로 문이 종료한 위치를 파악하고 순차적으로 하나씩 문을 실행한다.

그런데 문의 끝에 붙이는 세미콜론은 생략 가능하다. 세미콜론 자동 삽입 기능(ASI)이 암묵적으로 수행되기 떄문에.

세미콜론 자동 삽입 기능의 동작과 개발자의 예측이 일치하지 않는 경우가 간혹 있다. 주의할 것. - 링크

💬 세미콜론을 쓰자는 생각. 사실 여태껏 prettier가 알아서 다 해줬다. save 필수.

표현식인 문과 표현식이 아닌 문

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

문에는 표현식인 문과 표현식이 아닌 문이 있다. 표현식인 문은 값으로 평가될 수 있는 문, 표현식이 아닌 문은 값으로 평가될 수 없는 문이다. 표현식인 문과 표현식이 아닌 문을 구별하려면 변수에 할당해 보면 된다.

변수 선언문은 값으로 평가될 수 없다. 따라서 표현식이 아닌 문이다. 하지만 할당문은 값으로 평가될 수 있으므로 표현식인 문이다.

var foo = var a; // SyntaxError: Unexpected token var 

var x; 
x = 10; 

var y; 
y = x = 20; // 할당문은 표현식인 문이기 때문에 값처럼 사용 가능

x = 20은 x에 할당한 값 20으로 평가되어 y에는 20이 할당된다.

데이터 타입

데이터 타입은 값의 종류를 말한다. 자바스크립트의 모든 값은 데이터 타입을 갖는다.

개발자는 명확한 의도를 가지고 타입을 구별해서 값을 생성할 것이고, 자바스크립트 엔진은 타입을 구별해서 값을 취급할 것이다. (런타임에)

최신 ECMAScript 표준은 8가지 데이터 타입을 정의한다.

[문법과 자료형 - JavaScript MDN](https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Grammar_and_types#%EB%8D%B0%EC%9D%B4%ED%84%B0%EA%B5%AC%EC%A1%B0%EB%B0%8F_%ED%98%95)

숫자 타입

자바스크립트의 숫자 타입의 값은 배정밀도 64비트 부동소수점 형식을 따른다. 모든 수를 실수로 처리한다.

💬 부동 소수점.. 배웠는데 까먹었다. JS에서의 64비트 부동소숫점

정수로 표시된다 해도 사실은 실수이다.

console.log(1 === 1.0) // true

숫자 타입은 추가적으로 특별한 값을 가진다.

  • Infinity: 양의 무한대

    • 1 / 0; // Infinity
      
  • -Infinity: 음의 무한대

  • NaN: 산술 연산 불가(not-a-number)

    • 연산 결과 에러가 발생했다는 의미. 숫자가 아닌 값을 변환하려고 할 때 나타남

    • 'a' / 2 // NaN
      

자바스크립트는 대소문자를 구별하므로 주의할 것.

문자열 타입

문자열 타입은 텍스트 데이터를 나타내는 데 사용한다. 문자열은 0개 이상의 16비트 유니코드 문자(UTF-16)의 집합으로 전 세계 대부분의 문자를 표현할 수 있다.

💬 유니코드.. 일단 링크만 남기고 나중에 다시 확인. 유니코드를 제대로 이해하는 방법

문자열은 작은따옴표(‘’), 큰따옴표(“”) 또는 백틱(``)으로 텍스트를 묶는다. 그 이유는 문자열을 따옴표로 감싸 키워드나 식별자 같은 토큰과 구별하기 위해서. 그리고 공백 문자도 포함하기 위해서도.

큰따옴표와 작은따옴표는 기본적인 따옴표로, 자바스크립트에서는 이 둘에 차이를 두지 않는다.

var string;
string = '작은따옴표로 내의 "큰따옴표"는 문자열';
string = "큰따옴표 내의 '작은따옴표'는 문자열";

템플릿 리터럴

템플릿 리터럴은 멀티라인 문자열, 표현식 삽입, 태그드 템플릿 등 편리한 문자열 처리 기능을 제공한다. 런타임에 일반 문자열로 변환되어 처리된다. 백틱(``)을 사용해 표현한다.

멀티라인 문자열

일반 문자열 내에서는 줄바꿈(개행)이 허용되지 않는다. 줄바꿈 등의 공백을 표현하려면 백슬래시()로 시작하는 이스케이프 시퀀스를 사용해야 한다. - 이스케이프 시퀀스

템플릿 리터럴 내에서는 이스케이프 시퀀스를 사용하지 않고도 줄바꿈과 같은 공백이 있는 그대로 유지된다.

var string = `
-----------
 H E L L O
-----------
`

표현식 삽입

문자열은 문자열 연산자 +를 사용해 연결할 수 있다. 피연산자 중 하나 이상이 문자열인 경우 문자열 연결 연산자로 동작한다. 문자열을 조합할 때 유용.

console.log('Hello ' + 'world!'); // Hello world!

표현식을 삽입하려면 ${}으로 표현식을 감싼다. ${ } 안의 자바스크립트 표현식을 평가한 결과가 문자열로 취급된다.

var first = 'Brendan'
var last = 'Eich'
console.log(`Hello, ${first} ${last}`); // Hello, Brendan Eich

태그드 템플릿 리터럴

💬 예전에 작성했던 styled component 동작 원리 게시글로 대체

불리언 타입

논리적 참과 거짓을 나타내는 true, false 두 가지뿐이다. 긍정이나 부정을 나타내는 값을 저장하거나 비교 결과를 저장할 때 , 조건문에서 사용한다.

let nameFieldChecked = true;

let isGreater = 4 > 1; // true

undefined 타입

undefined는 개발자가 의도적으로 할당하기 위한 값이 아니라 자바스크립트 엔진이 변수를 초기화할 때 사용하는 값이다.

변수는 선언했지만, 값을 할당하지 않았다면 undefined가 할당된다고 했다. 변수를 선언하고 값을 할당하지 않은 변수를 참조하면 undefined가 반환된다.

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

의도적으로 변수에 undefined를 할당할 수는 있지만 초기화할 때 사용하는 값으로 사용하는 취지와 어긋난다. 변수에 값이 없음을 명시하고 싶을 때는 null을 할당한다.

null 타입

자바스크립트에서 null은 존재하지 않는 값, 비어 있는 값, 알 수 없는 값을 나타내는 데 사용한다.

변수에 null을 할당하는 것은 아무것도 참조하지 않는다, 값이 존재하지 않는다는 뜻이다.

let age = null; // 나이를 알 수 없거나 값비어있음

또는, 함수가 유효한 값을 반환할 수 없는 경우 null을 반환하기도 한다.

<script>
  var element = document.querySelector('.box'); // box 클래스가 없다
  console.log(element); // null
</script>

null과 undefined 차이

null값이 없음을 나타낼 때 사용하는 특별한 값. 의도적으로 값이 비어있음을 명시하기 위해 사용.

undefined초기화되지 않은 변수의 값. (+) 존재하지 않는 객체 프로퍼티나 배열 요소에 접근했을 때 반환되는 값.

typeof null

null은 기본 자료형이다. typeof null의 값은 null 이겠지 예상하지만 실제로는 ‘object’이다. 이유는 아래 문서를 참고.

javascript - Why is typeof null “object”? - Stack Overflow

심벌 타입

심벌(Symbole)은 변경 불가능한 원시 타입의 값이다. 다른 값과 중복되지 않는 유일무이한 값으로 주로 이름이 충돌할 위험이 없는 객체의 유일한 프로퍼티 키를 만들기 위해 사용한다.

생성방법은 리터럴이 아닌 Symbol 함수를 호출해 생성. 이때 생성된 심벌 값은 외부에 노출되지 않으며, 다른 값과 절대 중복되지 않는다.

심벌의 사용 예시를 살펴보자. Counter라는 함수를 만들어 외부 라이브러리로 배포했다고 가정하자.

// counter.js
const count = Symbol();

export default class Counter {
    [count] = 0;
    add() {
        this[count] += 1;
        return this;
    }

    get() { return this[count]; }
}
// index.js
import Counter from './counter.js';

const counter = new Counter();
counter.count = 5;
console.log(counter.get()); // 0

자바스크립트 코드에서 생성자 함수를 사용해 인스턴스를 생성했다. counter.js의 count 값에 접근해서 값을 변경하지만 반영되지 않았다.

심벌값이 외부에 노출되지 않기 때문에 index.js 파일에서 count 값을 변경할 수 없다.

💬 자바스크립트 Symbol, 어디에 쓸까요? - YouTube

객체 타입

자바스크립트는 객체 기반의 언어이며, 자바스크립트를 이루고 있는 거의 모든 것이 객체이다.

하나의 값만 담을 수 있는 원시 타입과 달리 객체는 다양한 값을 담을 수 있다. 객체는 0개 이상의 프로퍼티로 구성된 집합이며, 프로퍼티는 키(key)와 값(value)으로 구성된다.

var person = {
    name: 'Lee',
    age: 20. 
}
  • name: 'Lee', age: 20: 프로퍼티

    • name, age: 프로퍼티 키

    • ‘Lee’, 20: 프로퍼티 값

자바스크립트에서 사용할 수 있는 모든 값은 프로퍼티 값이 될 수 있다. 함수도 값이므로 프로퍼티 값으로 사용할 수 있다. 프로퍼티 값이 함수일 경우, 일반 함수와 구분하기 위해 메서드(method)라 부른다.

var counter = {
    num: 0, // 프로퍼티
    increase: function() { this.num++; } // 메서드
}
  • 프로퍼티: 객체의 상태를 나타내는 값(data)

  • 메서드: 프로퍼티(상태 데이터)를 참조하고 조작할 수 있는 동작(behavior)

객체는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 메서드를 포함할 수 있기 때문에 ‘상태’와 ‘동작’을 하나의 단위로 구조화할 수 있어 유용하다.

객체는 변경 가능한 값이다.

원시 타입의 값은 변경 불가능한 값이다. “변수는 재할당을 통해 변수 값을 변경할 수 있잖아?” 그렇다. 그렇기 때문에 변수라고 부른다.

변경 불가능하다는 것은 변수가 아니라 값에 대한 진술이다. 변수에 새로운 원시 값을 재할당하면 새로운 메모리 공간을 확보하고 재할당한 원시 값을 저장한 후, 변수는 새롭게 재할당한 원시 값을 가리킨다. 그 이유가 원시 값이 변경 불가능한 값이기 때문이다.

변수 값을 변경하는 것이 아니라 ‘교체’하는 것이다. 변경 가능한 값이라면 원래 가리키고 있던 메모리 공간의 값만 변경하면 된다.

변수 값을 변경하기 위해 원시 값을 재할당하면 새로운 메모리 공간을 확보하고 재할당한 값을 저장한 후, 변수가 참조하던 메모리 공간의 주소를 변경한다. 값의 이러한 특성을 불변성이라 한다. 이는 값의 상태 변경을 어렵게 만든다.

변하지 않는 상태를 유지하는 방법, 불변성(Immutable)

객체는 변경 가능한 값이다. 원시 값을 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 원시 값에 접근할 수 있다. 즉, 원시 값을 할당한 변수는 원시 값 자체를 값으로 갖는다.

하지만 객체를 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 참조 값에 접근할 수 있다. 참조 값은 생성된 객체가 저장된 메모리 공간의 주소이다. 객체를 할당한 변수를 참조하면 참조 값을 통해 실제 객체에 접근한다.

// person 변수가 객체 { name: 'Lee' }를 참조하고 있
var person = {
   name: 'Lee',
}

객체는 변경 가능한 값이다. 객체를 할당한 변수는 재할당 없이 객체를 직접 변경할 수 있다.

var person = {
   name: 'Lee',
}

person.name = 'Kim';
person.address = 'Seoul'

객체는 프로퍼티의 개수가 정해져 있지 않으며, 동적으로 추가되고 삭제할 수 있다. 프로퍼티의 값의 제약도 없다. 따라서 객체는 메모리 공간의 크기를 사전에 정해 둘 수 없다.

객체는 원시 값과 비교해 관리하는 방식이 복잡하고 구현 방식도 브라우저 제조사마다 다를 수 있다. 객체는 경우에 따라 크기가 매우 클 수도 있고 원시 값에 비해 생성과 접근의 비용이 많이 든다.

💬 자바스크립트의 객체 관리 방식이 궁금해졌으나 KEEP…

객체의 크기가 매우 클 수도 있고, 값의 크기가 일정하지도 않으며 프로퍼티 값이 객체일 수도 있어서 복사해서 생성한다면 메모리를 효율적으로 사용할 수 없고 성능이 나빠진다.

메모리 사용의 효율성과 성능을 위해 객체는 변경 가능한 값으로 설계되어 있다.

데이터 타입의 필요성

데이터 타입에 의한 메모리 공간의 확보와 참조

값은 메모리에 저장하고 참조할 수 있어야 한다. 메모리에 값을 저장하려면 먼저 확보해야 할 메모리 공간의 크기를 결정해야 한다.

데이터 타입은 값에 따라 런타임에 결정된다고 했다. 예를 들어 살펴보자.

var score = 80;

숫자 값 80을 저장하기 위해 먼저 메모리를 확보해야 한다. ECMAScript 사양에 따르면 숫자 타입은 8바이트로 명시되어 있다. 자바스크립트 엔진은 리터럴 80을 숫자 타입의 값으로 해석하고 숫자 타입을 저장하기 위해 8바이트의 메모리 공간을 확보한다. 그리고 80을 2진수로 저장한다.

이제 값을 참조해보자. 식별자 score는 값이 저장된 메모리 주소가 있다. score를 통해 값이 저장된 선두 메모리 셀의 주소를 찾아간다. 이제 얼마만큼 메모리를 읽어야 하는가?

데이터 타입마다 확보하는 메모리 공간이 다르다. 따라서 값을 참조할 때 데이터 타입이 확보한 메모리 공간의 크기만큼 읽어야 값이 훼손되지 않는다.

데이터 타입은 한 번에 읽어 들여야 할 메모리 셀의 크기를 알려주는 지표이다.

💬 식별자를 키로 바인딩된 값의 메모리 주소, 게이터 타입, 스코프 등을 어떻게 관리하는지 ‘심벌 테이블’을 학습하면 알 수 있을 것.

데이터 타입에 의한 값의 해석

메모리를 얼마나 읽어야 하는지는 알았다. 그렇다면 메모리에서 읽어 들인 2진수는 어떻게 해석해야 하는가?

모든 값은 데이터 타입을 가지며 2진수, 즉 비트의 나열로 저장된다. 메모리에 저장된 값은 데이터 타입에 따라 다르게 해석될 수 있다. 0100 0001은 숫자로 해석하면 65지만 문자열로 해석하면 ‘A’다.

변수의 데이터 타입을 보고 읽어 들인 2진수를 해당 데이터 타입으로 해석한다. “너는 숫자 타입이군.”

데이터 타입이 필요한 이유를 정리하자면

  • 값을 저장할 때 확보해야 하는 메모리 공간의 크기를 결정하기 위해

  • 값을 참조할 때 한 번에 읽어 들여야 할 메모리 공간의 크기를 결정하기 위해

  • 메모리에서 읽어 들인 2진수를 어떻게 해석할지 결정하기 위해

동적 타이핑

동적 타입 언어와 정적 타입 언어

정적 타입 언어는 코드 실행 전에 모든 변수의 타입을 결정한다. 변수를 선언할 때 데이터 타입을 미리 선언하는데 이를 명시적 타입 선언이라 한다.

char c;
int num;

정적 타입 언어는 변수의 타입을 변경할 수 없고, 선언한 타입에 맞는 값만 할당할 수 있다. 컴파일 시점에 타입 체크를 수행해 통과하지 못하면 에러를 발생시키고 프로그램 실행을 막는다. 타입의 일관성을 강제함으로써 더욱 안정적인 코드의 구현을 통해 런타임에 발생하는 에러를 줄이는 것.

동적 타입 언어는 변수의 타입을 코드가 실행 되는 도중에 결정한다. 선언이 아닌 할당에 의해 타입이 결정(타입 추론)되고 재할당에 의해 변수의 타입은 언제든지 동적으로 변할 수 있다. 이러한 특징을 동적 타이핑이라고 한다.

동적 타입 언어와 변수

동적 타입 언어는 변수에 어떤 데이터 타입의 값이라도 자유롭게 할당할 수 있다. 미리 변수에 타입을 설정하지 않아도 된다는 유연함이 장점이지만 위험이 도사리고 있다.

변수 값은 언제든지 변경될 수 있기 때문에 변화하는 변수 값을 추적하기 어려울 수 있다. 변수의 타입이 동적으로 변하기 때문에 타입도 언제든지 변경될 수 있다.

동적 타입 언어는 유연성은 높지만 신뢰성은 떨어진다.

💬 변수를 사용할 때 주의 사항은 다음에 변수명 짓기와 가독성 좋은 코드를 추가로 알아보면서 학습해보기. KEEP

부족한 내용 / 궁금한 내용

  • 식별자 매핑 관계를 어떻게 관리하는지
  • 부동 소수점 표기법
  • 유니코드
  • 메모리 관리 - 변수 선언, 할당, 재할당, 원시 타입/객체 타입
  • 불변성이 필요한 이유

느낀점

“공식적 시간이 9 to 9이라지만 식시시간 2시간 빼고 10시간인데 그만큼 하겠어?” 싶었는데 하게 되네… 오히려 시간이 모자란 느낌도 든다. 저녁 9시가 넘어도 붙잡고 있게 된다.

“자바스크립트의 기본기를 다지겠어”가 목표였는데 Deep Dive하고 있다. 한 개념이 다른 개념을 부르고 이해하고 넘어가려다 보니 한 챕터 넘기기도 쉽지 않다.

특히 메모리 관점에서 변수를 선언하고 할당하고 재할당하는 이미지가 잘 그려지지 않는다. 어렴풋이 이해는 가지만 명확하게 정리되지 않는다. 멘토님께 면담을 신청해야겠다.

오늘 학습한 데이터 타입은 특히 다른 프로그래밍 언어를 학습하면서라도 많이 친숙한 개념인데 특히 시간이 오래 걸렸다. 개념과 사용 예시, 동작 원리, 필요성 등 가지 뻗치듯 확장되어 “이게 맞나..”라는 생각이 저절로 들었다.

분명 중요한 자산이지만 학습해야 할 목표 분량이 있기 때문에 덮어두고 가는 지혜도 필요하다. 표시해두거나 레퍼런스를 달아놓고 이후에 다시 학습하는 것이 좋겠다.

부족한 내용/궁금한 내용을 잘 활용하기.

카테고리:

업데이트:

댓글남기기