CodeStates/JavaScript

Section1 / Unit9 : 참조 자료형

yeeendy 2023. 4. 27. 09:14

코드스테이츠 수업 13일차!

 

정리할 내용

자바스크립트의 자료형 & 얕은 복사와 깊은 복사

 

 

JavaScript 자료형(type)
  • JavaScript에서 자료형(type)이란 값(value)의 종류
  • 자료형은 크게 두 가지로 구분할 수 있는데, 바로 원시 자료형(primitive type)참조 자료형(reference type)
    ✔️원시 자료형(primitive type)
    : 6개의 자료형(number, string, boolean, undefined, null, symbol)
    ✔️참조 자료형(reference type)
    : 원시 자료형이 아닌 모든 자료형. 배열, 객체, 함수가 대표

 

원시 자료형과 참조 자료형의 특징
원시 자료형 참조 자료형
원시 자료형을 변수에 할당하면 메모리 공간에 값 자체가 저장된다. 참조 자료형을 변수에 할당하면 메모리 공간에 주소값이 저장된다.
원시 값을 갖는 변수를 다른 변수에 할당하면 원시 값 자체가 복사되어 전달된다. 참조 값을 갖는 변수를 다른 변수에 할당하면 주소값이 복사되어 전달된다.
원시 자료형은 변경 불가능한 값(immutable value)이다. 즉, 한 번 생성된 원시 자료형은 읽기 전용(read only) 값이다. 참조 자료형은 변경이 가능한 값(mutable value)이다.
// 원시 자료형이 참조된 변수를 다른 변수에 할당하기
let num = 20;
let copiedNum = num;

// 참조 자료형이 할당된 변수를 다른 변수에 할당하기
let arr = [0, 1, 2, 3];
let copiedArr = arr;

// 두 변수가 같은지 확인하기 - 1
console.log(num === copiedNum); // true
console.log(arr === copiedArr); // true

// 원본을 변경하기
num = 30;
arr.push(4);

// 두 변수가 같은지 확인하기 - 2
console.log(num === copiedNum); // false
console.log(arr === copiedArr); // true

// 이 두 변수의 현재 상태는?
console.log(copiedNum); // 20
console.log(copiedArr); // [ 0, 1, 2, 3, 4 ]
  • 문자열도 원시 자료형이기 때문에 값을 변경할 수 없다.(아래 예제)
let str = 'code';
str = 'states';

str[5] = 'z';

console.log(str); //states

 

 

배열 복사하는 2가지 방법
  • slice( )
  • spread 문법

 

slice( )
  • 배열 내장 메서드
  • 원본 배열을 복사
//새롭게 생성된 배열은 원본 배열과 같은 요소를 갖지만 참조하고 있는 주소는 다르다

let arr = [0, 1, 2, 3];
let copiedArr = arr.slice();
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]

 

 

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

//배열의 요소가 같아도, 참조 자료형이기 때문에 각각 다른 주소를 참조
let num = [1, 2, 3];
let int = [1, 2, 3];
console.log(num === int) // false


//새로운 배열 안에 원본 배열을 펼쳐서 전달하면
//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]

 

 

객체 복사하기
  • 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
  • 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)
  • slice(), Object.assign(), spread syntax 등의 방법으로 참조 자료형을 복사하면, 중첩된 구조 중 한 단계까지만 복사
let users = [
	{
		name: "kimcoding",
		age: 26,
		job: "student"
	},
	{
		name: "parkhacker",
		age: 29,
		job: "web designer"
	},
];
let copiedUsers = users.slice();

console.log(users === copiedUsers); // false

//users[0]과 copiedUsers[0]는 여전히 같은 주소값을 참조하고 있기 때문
console.log(users[0] === copiedUsers[0]); // true

 

깊은 복사(deep copy)
  • 참조 자료형 내부에 중첩되어 있는 모든 참조 자료형을 복사하는 것
  • JavaScript 내부적으로는 깊은 복사를 수행할 수 있는 방법이 없습니다.
  • 단, JavaScript의 다른 문법을 응용하면 깊은 복사와 같은 결과물을 만들어 낼 수 있습니다.
    ✔️JSON.stringfy()
    ✔️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

//예외
//중첩된 참조 자료형 중에 함수가 포함되어 있을 경우 위 방법을 사용하면 함수가 null로 바뀜
const arr = [1, 2, [3, function(){ console.log('hello world')}]];
const copiedArr = JSON.parse(JSON.stringify(arr));

console.log(arr); // [1, 2, [3, function(){ console.log('hello world')}]]
console.log(copiedArr); // [1, 2, [3, null]]
console.log(arr === copiedArr) // false
console.log(arr[2] === copiedArr[2]) // false
  • 외부 라이브러리 사용
    ✔️완전한 깊은 복사를 반드시 해야 하는 경우라면, node.js 환경에서 외부 라이브러리인 lodash, 또는 ramda를 설치하면 됩니다.
        lodash와 ramda는 각각 방법으로 깊은 복사를 구현해 두었습니다.
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