[코어 자바스크립트 요약] 01. 데이터 타입

·2024년 1월 30일
0

FE

목록 보기
2/3
⚠️ [1] 기본형 타입과 참조형 타입이 서로 다르게 동작하는 이유를 이해하며, 적절히 활용한다. [2] 데이터 타입과 관련된 개념에 대해 이해한다.

01. 데이터 타입의 종류

기본형 primitive type

숫자 number, 문자열 string, 불리언 boolean, null, undefined, 심볼 symbol (ES6)

  • 할당이나 연산 시 값이 담긴 주솟값을 바로 복제함.
  • 불변성(immutuability)

참조형 reference type

객체 object, 배열 array, 함수 function, 날짜 date, 정규표현식 RegExp

할당이나 연산 시 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주소값을 복제함.

02. 데이터 타입에 관한 배경지식

1-2-1 메모리와 데이터

  • 비트: 0 또는 1만 표현할 수 있는 하나의 메모리 조각
  • 바이트(byte): 8비트, 8개의 비트로 구성

C/C++ 혹은 JAVA는 2바이트(short), 4바이트(int)

Javascript는 숫자의 경우 8바이트를 확보(정수형과 부동소수형 구분을 하지 않음)

모든 데이터는 바이트 단위의 식별자, 더 정확히는 메모리 주솟값을 통해 서로 구분 및 연결이 가능

1-2-2 식별자와 변수

  • 변수: 변할 수 있는 데이터
  • 식별자: 변수명

03. 변수 선언과 데이터 할당

1-3-1 변수 선언

변수: 변경 가능한 데이터가 담길 수 있는 공간 또는 그릇

var a;

Untitled

[1] 변수 선언: 컴퓨터는 메모리에서 비어있는 공간 하나를 확보, 임의로 1003번으로 정함.
이 공간의 이름(식별자)을 a라고 지정.

[2] a에 접근: 컴퓨터는 메모리에서 a라는 이름을 가진 주소를 검색해 해당 공간에 담긴 데이터를 반환

1-3-2 데이터 할당

a = 'abc'; // 할당

Untitled

[1] 데이터 영역의 빈공간(@5004)에 문자열 ‘abc’를 저장

[2] 변수 영역에서 a라는 식별자를 검색(@1003)

[3] 앞서 저장한 문자열의 주소 @5004를 @1003의 공간에 대입

  • @왜 변수 영역에 값을 직접 대입하지 않고 굳이 번거롭게 단계를 더 거치는가? → 데이터 변환을 자유롭게 + 메모리를 더욱 효율적으로 관리하기 위한 고민의 결과
  • JS에서 문자열은 특별히 정해진 규격이 없다. 문자열 변수는 가변적으로 데이터 영역을 차지한다.
  • @미리 확보한 공간 내에서만 데이터 변환을 할 수 있다면? → 확보된 공간을 변환된 데이터 크기에 맞게 늘리는 작업이 선행되어야 할 것 → 따라서 변수 영역, 데이터 영역을 분리하는 게 효율적

Untitled

@문자열 ‘abc’의 마지막의 ‘def’를 추가하자

[1] ‘abcdef’라는 문자열을 새로 만들어 별도의 공간(@1003)에 저장

[2] 변수 영역에서 a라는 식별자를 검색(@1003)

[3] 앞서 저장한 문자열의 주소 @5005를 @1003의 공간에 대입

[4]@5004는 (자신의 주소를 저장하는 변수가 하나도 없다면) 가비지 콜렉터가 수거

@500개의 변수를 생성해 모든 변수에 숫자 5를 할당

→ 데이터 영역이 없다면, 주소 공간 2바이트500개 + 데이터 공간 8바이트500개

→ 데이터 영역이 있으므로 중복 참조해 주소 공간 2바이트500개 + 데이터 공간 8바이트1개

→ IF 변수 중 하나를 다른 숫자로 할당?

⇒ 다른 숫자용으로 데이터 영역 하나 차지, 주소 공간에 주소만 대입

→ → 따라서 변수 영역, 데이터 영역을 분리하면 중복된 데이터에 대한 처리 효율도 높아짐.

04. 기본형 데이터와 참조형 데이터

1-4-1 불변값

  • 변수: 변수 영역 메모리 변경 가능
  • 상수: 변수 영역 메모리 변경 불가
  • 불변값: 데이터 영역 메모리 변경 불가. 변경은 새로 만드는 동작을 통해서만 이뤄짐.

@기본형 데이터는 모두 불변값.

기본형 데이터 ← 숫자, 문자열, boolean, null, undefined, Symbol

var a = "abc"; // [1]
a = a + "def"; // [1]

var b = 5;     // [2]
var c = 5;     // [3]
b = 7;         // [4]

[1] 변수 a에 문자열 ‘abc’를 할당 뒤에 ‘def’를 추가하면 새로운 ‘abcdef’를 만들어 그 주소를 변수 a에 저장

[2] 데이터 영역에서 5를 찾고, 없으면 그제서야 데이터 공간을 하나 만들어 저장, 해당 데이터 공간의 주소를 b에 저장

[3] 이미 만들어둔 데이터 공간의 주소를 c에 저장

[4] 기존에 저장해둔 7의 데이터 공간의 주소를 찾아서 있으면 재활용, 없으면 새로 만들어서 b에 저장.

1-4-2 가변값

  • 참조형 데이터의 기본적인 성질은 가변값.
    • 이때 가변은 참조형 데이터 자체를 변경할 경우가 아니라 그 내부의 프로퍼티를 변경할 때만 성립
  • 객체의 변수 영역이 별도로 존재
  • 설정에 따라 변경 불가능하게 할 수 있고(Object.defineProperty 등), 불변값으로 활용 가능
var obj1 = {
  a: 1,
  b: 'bbb'
};

Untitled

[1] 변수 영역 빈 공간 @1002를 확보, 그 주소의 이름을 obj로 지정

[2] 데이터 → 여러 개의 프로퍼티로 이뤄진 데이터 그룹
해당 그룹 내의 프로퍼티들을 저장 위해 별도의 변수 영역을 마련, 그 영역의 주소를 @5001에 저장.

[3] @7103 및 @7104에 각각 a와 b라는 프로퍼티 이름을 지정

[4] 데이터 영역에서 숫자 1 검색, 검색 결과가 없으므로 임의로 @5003에 저장, 이 주소를 @7103에 저장. 문자열 ‘bbb’ 역시 임의로 @5004에 저장, 이 주소를 @7104에 저장

@참조형 데이터의 프로퍼티 재할당

var obj1 = {
	a: 1,
	b: 'bbb',
};

obj1.a = 2;

Untitled

[1] 데이터 영역에서 숫자 2 검색

[2] 검색 결과가 없음, 빈 공간인 @5005에 저장, 이 주소를 @7103에 저장

[3] 이 주소를 @7103에 저장. @7104에 이름 arr를 지정
→ 즉 새로운 객체가 아닌 기존의 객체 내부의 값만 바뀐 것!

@중첩된 참조현 데이터(객체)의 프로퍼티 할당

Untitled

@1002 → @5001 → (@7103 ~ ?) → @7104 → @5003 → (@8104 ~ ?) → @8105 → @5004 → 4 반환

@여기서 재할당 명령을 아래와 같이 내리면?

obj.arr = 'str';

Untitled

@5003은 더 이상 자신의 주소를 참조하는 변수가 하나도 없게 됨
→ GC 수거 대상이 됨.

  • 참조 카운트: 어떤 데이터에 대해 자신의 주소를 참조하는 변수의 개수
  • 가비지 컬렉터: 런타임 환경에 따라 특정 시점이나 메모리 사용량이 포화 상태에 임박할 때마다 자동으로 수거 대상들을 수거(collecting)
    • 수거된 메모리는 다시 새로운 값을 할당할 수 있는 빈 공간이 됨
  • 참조 카운트가 0인 메모리 주소는 가비지 컬렉터의 수거 대상이 됨.

1-4-3 변수 복사 비교

기본형과 참조형의 복사 과정은 동일, 데이터 할당 과정에서 이미 차이 발생, 변수 복사 이후의 동작에도 큰 차이 발생

var a = 10;
var b = a;

var obj1 = {c:10, d:'ddd'};
var obj2 = 'obj1';

Untitled

@기본형 데이터

@1002 확보, 식별자=b로 지정

식별자 a를 검색해서 @1001에 저장된 값인 @5001을 들고 좀 전에 확보해둔 @1002에 값으로 대입

@참조형 데이터

@1004 확보, 식별자=obj2로 지정

식별자 obj1을 검색해(@1003) 그 값인 @5002를 들고 @1004에 값으로 대입

@변수 복사 이후 값 변경 결과 비교 - 객체의 프로퍼티 변경 시

var a = 10;
var b = a;
var obj1 = {c:10, d:'ddd'};
var obj2 = obj1;

b = 15;
obj2.c = 20;

Untitled

  • 기본형: 변수 a와 b는 서로 다른 주소를 바라보게 됨
  • 참조형: 변수 obj1와 obj2는 여전히 같은 객체를 바라보고 있는 상태.
  • 결국 코드로 표현하면,
    a !== b
    obj1 === obj2

@변수 복사 이후 값 변경 결과 비교 (2) - 객체 자체를 변경했을 때

var a = 10;
var b = a;
var obj1 = { c:10, d:"ddd"};
var obj2 = obj1;

b = 15;
obj2 = { c:20, d:"ddd"};

Untitled

  • obj2에도 새로운 객체를 할당해서 값을 직접 변경.
  • 참조형 데이터가 가변값이라고 설명할 때의 가변은 참조형 데이터 자체를 변경할 경우가 아니라 그 내부의 프로퍼티를 변경할 때만 성립.

05. 불변 객체

1-5-1 불변 객체를 만드는 간단한 방법

불변 객체 immutable object

  • 데이터 자체를 변경하고자 하면 기본형 데이터와 마찬가지로 기존 데이터는 변하지 않음
  • 내부 프로퍼티를 변경할 필요가 있을 때마다 매번 새로운 객체를 만들어 재할당하기로 규칙을 정하거나 자동으로 새로운 객체를 만드는 도구를 활용

@불변 객체가 필요한 경우

아래 예시에서 정보가 바뀐 시점에 알림을 보내야 함

바뀌기 전의 정보와 후의 정보의 차이를 가시적으로 보여줘야 하는 등의 기능을 구현해야 함

→ 변경 전, 후에 서로 다른 객체를 바라보게 만들어야 함.

var user = {
	name: "Jaenam",
	gender: 'male',
};

var changeName = function (user, newName) {
	var newUser = user;
	newUser.name = newName;
	return newUser;
};

var user2 = changeName(user, 'Jung');

if (user !== user2){
	console.log("유저 정보가 변경되었습니다.");
}

console.log(user.name, user2.name); // Jung Jung
console.log(user === user2);  // true

→ 위 코드를 고친 코드는 아래.

var user = {
	name: "Jaenam",
	gender: 'male',
};

var changeName = function (user, newName) {
	return {
		name: newName,
		gender: user.gender
	};
};

var user2 = changeName(user, 'Jung');

if (user !== user2){
	console.log("유저 정보가 변경되었습니다.");
}

console.log(user.name, user2.name); // Jaenam Jung
console.log(user === user2);  // false

→ 보다 효율적으로 바꾼 코드

var copyObject = function (target) {
	var result = {};
	for (var prop in target) {
		result[prop] = target[prop];
	}
	return result;
};

var user = {
	name: "Jaenam",
	gender: 'male',
};

var user2 = copyObject(user);
user2.name = "Jung"

if (user !== user2){
	console.log("유저 정보가 변경되었습니다.");
}

console.log(user.name, user2.name); // Jaenam Jung
console.log(user === user2);  // false

모두가 규칙을 따르지 않고는 프로퍼티 변경을 할 수 없게끔 시스템적으로 제약을 거는 편이 안전

→ 실제: immutable.js, baobab.js 등의 라이브러리 활용

1-5-2 얕은 복사와 깊은 복사

  • 얕은 복사 = shallow copy
    • 바로 아래 단계의 값만 복사
  • 깊은 복사 = deep copy
    • 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법

@객체의 깊은 복사를 수행하는 범용 함수

var copyObjectDeep = function(target) {
	var result = {};
	if (typeof target === "object" && target !== null) {
		for (var prop in target) {
			result[prop] = copyObjectDeep(target[prop]);
		}
	} else {
		result = target;
	}

	return result;
};

@다른 방법

  • hasOwnProperty 메서드를 활용해 프로토타입 체이닝, 상속된 프로퍼티를 복사하지 않게끔 할 수도 있음
  • JSON 활용
    • JSON 문법으로 표현된 문자열로 전환, 다시 JSON 객체로 바꿈
    • CF) 메서드(함수)나 숨겨진 프로퍼티(proto, getter/setter 등) JSON으로 변경 불가한 프로퍼티들은 모두 무시
    • httpRequest로 받은 데이터를 저장한 객체를 복사할 때 등 순수한 정보만 다룰 때 활용하기 좋은 방법

06. undefined와 null

undefined

  • 사용자가 명시적으로 지정 가능 접근 시
    → 이때의 프로퍼티나 배열의 요소는 고유의 키값(프로퍼티 이름)이 실존, 따라서 순회의 대상이 될 수 있음.
  • 값이 존재하지 않을 때 자바스크립트 엔진이 자동으로 부여하기도 함 접근 시
    → 이때의 해당 프로퍼티 내지 배열의 키값 자체가 존재하지 않음

[1] 값을 대입하지 않은 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근할 때

[2] 객체 내부의 존재하지 않는 프로퍼티에 접근하려고 할 때

[3] return 문이 없거나 호출되지 않는 함수의 실행 결과

// 자동으로 undefined를 부여하는 경우

var a;
console.log(a); // undefined, 1 - 값 대입하지 않은 변수

var obj = {a:1};
console.log(obj.a); // 1
console.log(obj.b); // undefined, 2 - 존재하지 않는 프로퍼티에 접근
console.log(b);     // c.f) Reference error - b is not defined

var func = function(){};
var c = func();     // undefined, 3 - return 값이 없으면 undefined를 반환한 것으로 간주
console.log(c);     // undefined

// undefined와 배열
var arr1 = [];
arr1.length = 3;
console.log(arr1); // [empty * 3]

var arr2 = new Array(3);
console.log(arr2); // [empty * 3]

var arr3 = [undefined, undefined, undefined];
console.log(arr3); // [undefined * 3]

// 빈 요소와 배열의 순회

var arr1 = [undefined, 1];
var arr2 = [];
arr2[1] = 1;

arr1.forEach(function(v, i){console.log(v,i);}); // undefined 0 / 1 1 
arr2.forEach(function(v, i){console.log(v,i);}); // 1 1 

arr1.map(function(v, i){return v+i;}); // [NaN, 2]
arr2.map(function(v, i){return v+i;}); // [empty, 2]

arr1.filter(function(v, i){return !v;}); // [undefined]
arr2.filter(function(v, i){return !v;}); // []

arr1.reduce(function(p, c, i){return p + c + i;}); // undefined011
arr2.reduce(function(p, c, i){return p + c + i;}); // 11

arr1은 배열의 모든 요소를 순회해 결과를 출력

arr2은 각 메서드들이 비어 있는 요소에 대해서는 어떠한 처리도 하지 않고 건너뜀

@배열도 객체

→ 존재하지 않는 프로퍼티에 대해서는 순회할 수 없음

→ 객체와 마찬가지로 특정 인덱스에 값을 지정할 때 비로소 빈 공간을 확보, 인덱스를 이름으로 지정하고 데이터의 주솟값을 저장하는 등의 동작을 함.

→ 사용자가 undefined를 명시적으로 부여한 경우 vs 비어있는 요소에 접근할 때 반환되는 경우 차이를 알 수 있음.

null

  • 사용 이유: undefined의 혼동을 피하기 위해, “비어있음”을 명시적으로 나타내고 싶을 때 사용 → undefined의 의미 = 값을 대입하지 않은 변수에 접근하고자 할 때 자바스크립트 엔진이 반환해주는 값으로만 존재할 수 있음
  • 주의: typeof null == object
// undefined vs null 비교
var n = null;
console.log(typeof n);       // object

console.log(n == undefined); // true
console.log(n == null);      // true

console.log(n === undefined); // false
console.log(n === null)       // true
  • 동등 연산자(equality operator) vs 일치 연산자(identity operator) 동등 연산자(==)는 두 피연산자의 값이 서로 같으면 참(true)을 반환합니다. 이때 두 피연산자의 타입이 서로 다르면, 비교를 위해 강제로 타입을 같게 변환합니다.
    하지만 일치 연산자(===)는 타입의 변환 없이 두 피연산자의 값이 같고, 타입도 같아야만 참(true)을 반환합니다. <예시>
    1 == true     => true
    true == true  => true
    1 === true    => false
    true === true => true

07. 정리

  • 기본형 = 불변값
  • 참조형 = 가변값
  • 변수 = 변경 가능한 데이터가 담길 수 있는 공간
  • 식별자 = 그 변수의 이름
profile
이것저것 개발하는 것 좋아하지만 서버 개발이 제일 좋더라구요..

0개의 댓글