typeof와 Data type
JavaScript에는 ‘typeof’라는 연산자가 있다. 이는 자료형을 문자열로 반환해주는 연산자로 코드를 만들 때 종종 사용하게 된다. 하지만 이 연산자를 쓸 때 가끔 생각과는 다른 결과가 종종 나오기도 한다. 이는 Data type과 연관이 있는데 어떠한 경우에 이런 현상이 나타나는지 왜 이런 현상이 발생하는지에 대해 포스팅 하려한다.
typeof
typeof 연산자는 피연산자의 평가 전 자료형을 나타내는 문자열을 반환합니다.
typeof 는 typeof operand 와 같은 형태로 사용된다. 이 때, operand란 자료형을 가져올 객체 또는 원시값을 나타내는 표현식을 뜻한다.
특이케이스
Number
1
2
3
4
// Numbers
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof NaN === 'number';
3.14와 같은 소수점이나 NaN 같은 타입을 보았을 때 혼동이올 수 있다. 하지만 type는 객체 또는 원시값이 기준이기 때문에 모두 number로 통일 된다.
typeof (typeof x)
1
2
// Strings
typeof (typeof 1) === 'string';
typeof를 typeof 연산자로 확인하면 return 값 기준으로 연산이 된다 생각할 수 있다. 하지만 typeof를 typeof로 연산할 때는 항상 ‘string’이 리턴된다.
null
1
typeof null === 'object';
null은 얼핏보면 ‘undefined’라고 생각할 수 있다. 하지만 null은 ‘object’ 리턴한다. 이는 자바스크립트가 처음 구현될 때 자바스크립트 값이 타입태그와 값으로 표시된 것이 이유이다. 객체의 타입 태그는 0이었으며 null은 Null Point(대부분의 플랫폼에서 0x00)으로 표시되었다. 때문에 null 또한 타입 태그로 0을 가지며 typeof가 object를 반환하는 결과를 초래하게 되었다.
Object
1
2
typeof new Date() === 'object';
typeof [1, 2, 4] === 'object';
Date()는 내장 함수이기 때문에 object를 반환한다. array의 typeof는 ‘object’이며 array를 판별하기 위해서는 Array.isArray( )를 사용하거나 Object.prototype.toString.call( )을 사용해야 한다.
Data type
array가 object를 반환하는 이유는 위에서 잠깐씩 설명했지만 이는 Data type과 큰 연관이 있다. Data type은 크게 Primitive type(기본/원시 타입)과 Reference type(참조 타입)으로 나뉘어진다. typeof 또한 반환값으로 위와 같은 타입으로 반환하기 때문에 이러한 현상이 발생하는 것이다. Primitive type과 Reference type에 대해 좀 더 알아보면 이해가 편해질 것이다.
Primitive type
Primitive type에 해당하는 값은 원시 값이 있다. JavaScript에서의 원시 값은 객체가 아니면서 메서드도 가지지 않는 데이터입이다. 따라서 string, number, bigint, boolean, symbol,undefined, null 총 7가지 타입이 이에 속하며 불변성을 띈다.
만약 우리가 a 라는 변수에 “123”이라는 값을 할당했을 경우 a라는 변수 위치에 값 “123”이 저장되는 것이 아니라 “123”이라는 값을 할당하기 위해 별도의 메모리 공간을 확보하여 저장하고 그 주소값을 a변수 영역에 저장하는 식으로 동작한다. 또한 a의 값을 “abc”로 재할당할 경우 별도의 공간에 “abc”를 저장하고 그 주소값을 a에 저장하는 식으로 변경이 이루어진다.
Reference type
Reference type에는 객체(Object)가 있으며 이 객체에는 array, function, Date 등이 속해 있다. 때문에 typeof array가 ‘object’를 반환하는 것이다.
참조형데이터는 변수영역, 데이터영역과 별도의 객체의 변수(property)영역이 존재하는 것이 특징이다. 때문에 변수 영역에서는 데이터영역의 주소값이 저장되고 데이터영역에는 프로퍼티영역의 주소값이 저장된다. 따라서 변수에 다른 값을 얼마든지 대입할 수 있으며 복사를 할 때 얕은 복사가 이루어 지기 때문에 이에 주의하며 쓰거나 깊은 복사로 불변 객체를 만들어야한다.
깊은 복사
깊은 복사에는 두가지 방법이 있다.
- 깊은 복사를 수행하는 함수를 이용
- JSON 문법으로 변환된 문자열로 변환했다가 JSON 객체로 변환
1번의 경우 내부 프로퍼티들을 복사해야 하며 이 과정을 참조형 데이터가 있을 때마다 재귀적으로 수행해야 한다. 따라서 아래의 코드를 이용하게 된다.
1
2
3
4
5
6
7
8
9
10
11
12
const copyObject = (target) => {
let result = {};
if (typeof target === 'object' && target !== null) {
for (var key in target) {
result[key] = copyObject(target[key]);
}
}
else {
result = target;
}
return result;
};
2번의 경우는 1번보다 간단하지만 JSON으로 변경할 수 없는 프로퍼티들은 무시가 된다. 때문에 순수한 정보만 다룰 때 활용하기 좋은 방법이다.
1
2
3
const copyObject = (target) => {
return JSON.parse(JSON.stringify(target));
};
차이점
기본형 데이터와 참조형 데이터의 차이는 불변성과 가변성에 있다기 보다는 주소값을 복사하는 과정의 차이에 있다. 위에 설명과 같이 기본형은 주소값을 한번만 복사하고 참조형은 한단계를 더 거치기 때문이다.
댓글남기기