데이터 타입

y0ung·2021년 5월 18일
0
post-thumbnail

1. 타입의 종류

원시형(기본형)

  • 숫자
  • 문자열
  • 불리언
  • null
  • undefined
  • 심볼(es6)

참조형

  • 객체
  • 배열
  • 함수
  • 날짜
  • 정규표현식
  • Map, WeakMap
  • Set, WeakSet

기본형과 참조형을 구분하는 방법은?

기본형참조형
- 할당이나 연산시 복제
- 값이 담긴 주솟값을 바로 복제
- 불변성
- 할당이나 연산시 복제
- 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제

2. 데이터 타입에 대한 배경지식

2-1 메모리와 데이터

컴퓨터는 모든 데이터를 0, 1로 바꿔 기억한다. 0 또는 1로 표한할수 있는 하나의 메모리 조각을 비트(bit)라고 한다.

각 비트는 고유한 식별자를 통해 위치를 확인 할수 있다. 바이트 역시 시작하는 비트의 식별자로 위치를 파악 할수 있다.

1byte = 8bit

모든 데이터는 바이트 단위의 식별자 즉, 메모리 주솟값을 통해 서로 구분하고 연결할 수 있다.

2-2 식별자와 변수

변수 : 변할수 있는수 (변할수 있는 데이터)

식별자 : 어떤 데이터를 식별하는데 사용하는 이름, 즉 변수 명

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

3-1 변수 선언

var a;

위의 예제를 말로 풀자면 "변할수 있는 데이터를 만든다. 이 데이터의 식별자는 a로 한다". 이다.

결국 변수란 변경 가능한 데이터가 담길수 있는 공간 또는 그릇이다.

3-2 데이터 할당

1.
var a;       // 변수 a 선언
a = 'abc';   // 변수 a에 데이터 'abc'할당

2.
var a = "abc" // 변수 선언과 할당을 한문장으로 표현

위와 같은 선언과 할당방법은 ?
1. 메모리에서 비어 있는 공간을 확보하고 그 공간의 이름을 a라고 이름을 설정
2. 이름 a 를 가진 주소를 검색해 그곳에 문자열 abc를 할당 한다.

그런데 실제로 해당 위치에 abc를 직접 저장 하지 않는다. 데이터 저장을 위해 별도의 메모리 공간을 다시확보해 문자열 abc를 저장하고, 그 주소를 변수 영역에 저장하는 식으로 이루어 진다.

직접 값을 대입하지 않고 번거롭계 한단계를 더 거치는 이유는?
데이터 변환을 자유롭게 할 수 있게 함과 동시에 메모리를 더욱 효율적으로 관리하기 위한 결과 이다.

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

4-1 불변값

var a = 5;
var b = 5;

b = 7;

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

위의 예제를 보면 변수 a,b 는 숫자 5를 할당 한다. 만약 5라는 숫자가 없다면 데이터 공간을 하나 만들어 저장하고, 있으면 해당 데이터주솟값을 저장한다.

마지막 줄에 b는 다시 7을 할당하는데 5 자체를 7로 바꾸는 것이 아니라 기존에 저장했던 7을 찾아서 있으면 재활용 하고, 없으면 새로 만들어 b에 저장한다. 결국 5 와 7 모두 다른값으로 변경할수 없다.

이와 같이 한번 만들어진 값은 가비지 컬렉팅을 당하지 않는한 영원히 변하지 않는다.

4-2 가변값

참조형 데이터는 성질에 따라 변경할수도 불변값 일수도 있다.

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

기본형의 데이터와의 차이는 객체의 변수 영역이 별도로 존재 한다는 점이다.

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

obj.a = 2;

obj1 이 바라보고 있는 주소 @5001은 변하지 않았았다. 즉 새로운 객체가 만들어 진 것이 아니라 기존의 객체 내부의 값만 바뀐것이다.


가비지 컬렉터 (garbage collector, GC)

어떤 데이터에 대해 자신의 주소를 참조하는 변수의 개수를 참조 카운트라고 한다. 참조 카운트가 있다면 1 이지만 없다면 0 이 된다. 참조 카운트가 0인 메모리 주소는 가비지 컬렉터의 수거 대상이 된다.
가비지 컬렉터는 메모리 사용량이 포화 상태에 임박할 때마다 자동으로 수거 대상들을 수거 한다. 수거된 메모리는 다시 새로운 값을 할당 할 수 있는 빈 공간이 된다.


4-3 변수 복사 비교

var a = 10;
var b = a;

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

5. 불변 객체

5-1 불변 객체 만들기

불변 객체가 왜 필요 할까?
값으로 전달받은 객체에 변경을 가하더라도 원본 객체는 변하지 않아야 하는 경우가 생길수 있다. 이럴때 불변객체가 필요 하다.

객체의 가변성에 따른 문제점
var user = {
  name:'ga young',
  age: 27
};

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

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

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

위의 예제는 가변성으로 인한 문제를 보여준다.
서로 다른 객체를 바라보게 만들면 이러한 문제를 해결할수 있다.

var user = {
  name:'ga young',
  age: 27
};

var changeName = (user, newName) =>{
  return {
	name: newName,
	age: user.age
  }
};

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

console.log(user.name, user2.name); // ga young Olive
console.log(user === user2); // false

changeName 함수가 새로운 객체를 반환하도록 수정했다. 이제 user와 user2는 서로 다른 객체 이므로 안전하게 변경 전과 후를 비교할 수 있다.

다만 아직 변경할 필요가 없는 기존 객체의 프로퍼티를 하드 코딩으로 입력했다. 이런 식으로는 대상 객체에 정보가 많을수록 사용자가 입력하는 수고가 늘어 날수 있다.

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

얕은복사

var copyObject = (target){
  var result = {};
  
  for(var prop in target){
	result[prop] = target[prop];
  }
}
var user = {
  name:'ga young',
  age: 27
};

var user2 = copyObject(user);
user2.name = 'Olive';

console.log(user.name, user2.name); // ga young Olive
console.log(user === user2); // false

copyoObject함수를 통해 간단하게 객체를 복사하고 내용을 수정할수 있었다. 하지만 협업하는 모든 개발자들이 user객체 내부의 변경이 필요할 때는 무조건 copyoObject 함수를 사용하기로 합의 하고 그 규칙을 지킨다는 전제하에서는 user객체가 곧 불변 객체라고 볼 수 있다.

개발자의 신뢰에만 의존하는것 보다는 모두가 그 규칙을 따르지 않고는 프로퍼티 변경을 할 수 없게끔 시스템적으로 제약을 거는 편이 안전하다.

얕은복사는 바로 아래 단계의 값만 복사 하는 방법이고 , 깊은 복사는 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법이다.

깊은 복사

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

target이 객체인 경우 내부 프로퍼티들을 순회하며 copyObjectDeep 함수를 재귀적으로 호출하고, 객체가 아닌 경우 target을 그대로 지정 하게끔했다. 이 함수를 사용해 객체를 복사한 다음에는 원본과 사본이 서로 완전히 다른 객체를 참조하게 되어 어느 쪽의 프로퍼티를 변경하더라도 다른 쪽에 영향을 주지 않는다.

var obj = {
  a: 1,
  b: {
    c: null,
    d: [1,2]
  }
}

var obj2 = copyObjectDeep(obj);

obj2.a = 3;
obj2.b.c = 4;
obj.b.d[1] = 3;

console.log(obj)  // {a: 1, b: {c: null, d:[1,3]}}
console.log(obj2) // {a: 3, b: {c: 4, d:[1,2]}}

6. undefined 와 null

둘다 '없음' 을 나타내지만 미세한 차이점이 있다.

undefined

  • 사용자가 명시적으로 지정할 수 있다.
  • 값이 존재하지 않을 때 자바스크립트 엔진이 자동으로 부여한다.

자바스크립트 엔진은 사용자가 어떤 값을 지정할 것이라고 예상되는 상황임에도 실제로는 그렇게 하지 않았을 때 undefined를 반환 한다. 다음 과 같은 경우가 이에 해당 한다.
1. 값을 대입하지 않은 변수, 즉 데이터 영역의 메모리 주소를 지정하지 않은 식별자에 접근 할때
2. 객체 내부의 존재하지 않는 프로퍼티에 접근하려고 할 때
3. return 문이 없거나 호출되지 않는 함수의 실행 결과

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) // 1 -> b is not defined

var foo = (){};
var c = foo() //return값이 없으면 undefined를 반환하는 것으로 간주
console.log(c) //  undefiend -> 3번 예시

null

같은 의미를 가진 null이라는 값을 이용해 비어 있음을 명시적으로 나타내면 된다.
추가로 null은 한가지 주의할 점이 있는데 type of null 이 object라는 점이다. 이는 자바스크립트 자체 버그 이다. 따라서 어떤 변수의 값이 null인지 여부를 판별하기위해서는 typeof대신 다른 방식으로 접근 해야 한다.

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

동등 연산자 (==) 이 아닌 더 정확히 비교하는 연산자인 일치 연산자(===) 를 이용해 null값을 판단 한다.


참고

해당 포스팅은 코어 자바스크립트 책의 내용을 공부, 정리한 내용입니다.

profile
어제보다는 오늘 더 나은

0개의 댓글