자바스크립트 자료형 정리

Plla2·2023년 3월 2일
0
post-thumbnail

자료형이란??

자료형이란, 값(value)의 종류이다.
자료형은 크게 두가지로 구분이 된다. 원시자료형, 참조 자료형이다.


원시자료형 vs 참조 자료형

원시자료형의 특징

  1. 원시 자료형을 변수에 할당하면 메모리 공간에 값 자체가 저장된다.
  2. 원시 값을 갖는 변수를 다른 변수에 할당하면 원시 값 자체가 복사되어 전달된다.
  3. 원시 자료형은 변경 불가능한 값(immutable value)이다. 즉, 한 번 생성된 원시 자료형은 읽기 전용(read only) 값이다.
  4. 자료형(number, string, boolean, undefined, null, symbol)을 원시 자료형으로 구분합니다.

참조 자료형의 특징

  1. 참조 자료형을 변수에 할당하면 메모리 공간에 주솟값이 저장된다.
  2. 참조 값을 갖는 변수를 다른 변수에 할당하면 주솟값이 복사되어 전달된다.
  3. 참조 자료형은 변경이 가능한 값(mutable value)이다.
  4. 여러 데이터를 한 번에 다룰 수 있는 배열, 객체가 대표적인 참조 자료형입니다. 함수도 참조 자료형으로 분류합니다.

그렇다면 만약, 객체 안에 객체가 있는 형태는 어떻게 될까??

let obj = {a=[4, 5, 6]}
위처럼 프로퍼티 명과 주소를 매치하기 위한 새로운 공간을 할당해보자.
이때, a 역시 객체이므로 a가 가르키는 주소에는 각 배열의 원소가 지정될 메모리 주소의 매치 정보가 저장되게 되고, 배열의 값은 각각의 주소에 따로 저장된다.

그렇다면 obj.a = 'new'와 같은 값 할당이 일어나면 어떻게 될까?
a가 가르키는 주소에는 'new'라는 데이터 값이 저장될 것이다. 이때, 각 원소들이 저장되어 있던 메모리 공간은 링크를 잃게 되어 galbage collerctor라고 하는 메모리 청소기의 청소 대상이 되어 삭제되게 된다.


예제를 보자

let a;
a= 10;

let b = 'abc'
b= false;

let c = b;
c=20;

위의 코드는 아래와 같이 정리를 할 수 있다.

따라서, 원시 자료형과, 참조 자료형을 정리해보면

  • 원시 자료형이 할당된 변수를 다른 변수에 할당하면 값 자체의 복사가 일어난다. 따라서 원본과 복사본 중 하나를 변경해도 다른 하나에 영향을 미치지 않는다.
  • 참조 자료형이 할당된 변수를 다른 변수에 할당하면 주소가 복사되어 원본과 복사본이 같은 주소를 참조한다
  • 참조 자료형의 주소값을 복사한 변수에 요소를 추가하면 같은 주소를 참조하고 있는 원본에도 영향을 미친다.
  • 참조 자료형이 저장된 변수를 다른 변수에 할당할 경우, 두 변수는 같은 주소를 참조하고 있을 뿐 값 자체가 복사 되었다고 볼 수 없다.

얕은 복사

배열복사

배열을 복사하는 방법은 크게 두 가지 방법이 있습니다. 배열 내장 메서드인 slice()를 사용하는 방법과 ES6에서 도입된 spread문법을 사용하는 방법입니다.

  1. slice()를 사용하여 원본 배열을 복사한다.
let arr = [0, 1, 2, 3];
let copiedArr = arr.slice();
console.log(copiedArr); // [0, 1, 2, 3]
console.log(arr === copiedArr); // false

새롭게 생성된 copiedArr 배열은 원본 배열과 같은 요소를 갖지만, 참조하고 있는 주소가 다르다.

copiedArr.push(4);
console.log(copiedArr); // [0, 1, 2, 3, 4]
console.log(arr); // [0, 1, 2, 3]

주소가 다르기 때문에 복사한 배열에 요소를 추가해도 원본배열에는 추가되지 않는다.

  1. spread syntax
    spread syntax는 ES6에서 새롭게 추가된 문법으로, spread라는 단어의 뜻처럼 배열을 펼칠 수 있습니다. 펼치는 방법은 배열이 할당된 변수명 앞에 ... 을 붙여주면 됩니다. 배열을 펼치면 배열의 각 요소를 확인할 수 있습니다.
let arr = [0, 1, 2, 3];

console.log(...arr); // 0 1 2 3

그렇다면 새로운 배열 안에 원본 배열을 펼쳐서 전달하면 어떻게 될까요? 원본 배열과 같은 요소를 가지고 있지만 각각 다른 주소를 참조하게 됩니다. 결과적으로 slice() 메서드를 사용한 것과 동일하게 동작합니다.

let arr = [0, 1, 2, 3];
let copiedArr = [...arr];
console.log(copiedArr); // [0, 1, 2, 3]
console.log(arr === copiedArr); // false

copiedArr.push(4);
console.log(copiedArr); // [0, 1, 2, 3, 4]
console.log(arr); // [0, 1, 2, 3]

객체 복사

  1. Object.assign()
    객체를 복사하기 위해서는 Object.assign()을 사용합니다.
let obj = { firstName: "coding", lastName: "kim" };
let copiedObj = Object.assign({}, obj);

console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
console.log(obj === copiedObj) // false

  1. spread syntax
    spread syntax는 배열뿐만 아니라 객체를 복사할 때도 사용할 수 있습니다.
let obj = { firstName: "coding", lastName: "kim" };
let copiedObj = {...obj};

console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
console.log(obj === copiedObj) // false

이처럼 slice(), Object.assign(), spread syntax 등의 방법으로 참조 자료형을 복사하면, 중첩된 구조 중 한 단계까지만 복사합니다. 이것을 얕은 복사(shallow copy)라고 합니다.


깊은 복사

반면, 참조 자료형 내부에 중첩되어 있는 모든 참조 자료형을 복사하는 것은 깊은 복사(deep copy)라고 합니다. 그러나 JavaScript 내부적으로는 깊은 복사를 수행할 수 있는 방법이 없습니다. 단, JavaScript의 다른 문법을 응용하면 깊은 복사와 같은 결과물을 만들어 낼 수 있습니다.

  1. JSON.stringify()와 JSON.parse()
    JSON.stringify()는 참조 자료형을 문자열 형태로 변환하여 반환하고, JSON.parse()는 문자열의 형태를 객체로 변환하여 반환합니다. 먼저 중첩된 참조 자료형을 JSON.stringify()를 사용하여 문자열의 형태로 변환하고, 반환된 값에 다시 JSON.parse()를 사용하면, 깊은 복사와 같은 결과물을 반환합니다.
const arr = [1, 2, [3, 4]];
const copiedArr = JSON.parse(JSON.stringify(arr));

console.log(arr); // [1, 2, [3, 4]]
console.log(copiedArr); // [1, 2, [3, 4]]
console.log(arr === copiedArr) // false
console.log(arr[2] === copiedArr[2]) // false

  1. 외부 라이브러리 사용
    완전한 깊은 복사를 반드시 해야 하는 경우라면, node.js 환경에서 외부 라이브러리인 lodash, 또는 ramda를 설치하면 됩니다. lodash와 ramda는 각각 방법으로 깊은 복사를 구현해 두었습니다. 다음은 lodash의 cloneDeep을 사용한 깊은 복사의 예시입니다.
const lodash = require('lodash');

const arr = [1, 2, [3, 4]];
const copiedArr = lodash.cloneDeep(arr);

console.log(arr); // [1, 2, [3, 4]]
console.log(copiedArr); // [1, 2, [3, 4]]
console.log(arr === copiedArr) // false
console.log(arr[2] === copiedArr[2]) // false

배열복사를 정리해보자면,

  • 배열의 경우 slice() 메서드 또는 spread syntax 등의 방법으로 복사할 수 있다.
  • 객체의 경우 Object.assign() 또는 spread syntax 등의 방법으로 복사할 수 있다.
  • 위 방법으로 참조 자료형을 복사할 경우, 중첩된 구조 중 한 단계까지만 복사된다. (얕은 복사)
  • JavaScript 내부적으로는 중첩된 구조 전체를 복사하는 깊은 복사를 구현할 수 없다. 단, 다른 문법을 응용하여 같은 결과물을 만들 수 있다.
  • 대표적인 JSON.stringify()와 JSON.parse()를 사용하는 방법이 있지만, 예외의 케이스가 존재한다. (참조 자료형 내부에 함수가 있는 경우)
  • 완전한 깊은 복사를 반드시 해야 하는 경우, node.js 환경에서 외부 라이브러리인 lodash, 또는 ramda를 사용하면 된다.
profile
코린이의 개미블로그

0개의 댓글