(자바스크립트)원시값과 참조값이란?

trina lee·2023년 4월 27일

원시값 (primitive value)

  • 숫자(number), 문자열(string), 불리언(boolean), null, undefined
  • 변수에 할당할 때 값 자체가 저장된다.
  • 원시값은 값 자체를 복사하여 변수에 할당할 수 있다.
  • 원시타입의 경우 call by value(값에 의한 호출, 값의 복사)로 동작한다.

참조값 (reference value)

  • 객체(object), 배열(array), 함수(function)
  • 변수에 할당할 때 값이 저장되는 것이 아니라, 해당 값이 위치한 메모리의 주소가 저장된다.
  • 참조값은 변수에 저장된 주소를 참조하여 실제 값을 수정하면 변수가 가르키는 값도 함께 변경된다. 이를 얕은 복사(shallow copy)라고 한다.
  • 참조타입의 경우 call by reference(참조에 의한 호출, 주소의 복사)로 동작한다.

call by value(값에 의한 호출, 값의 복사)

  • 값의 복사, 말 그대로 복사된 값을 인자로 넘겨서 매개변수로 전달한다.
  • 복사하여 처리하기 때문에 안전하다. 원래의 값이 보존된다.
  • 복사를 하기 때문에 메모리 사용량이 늘어난다.

call by reference(참조에 의한 호출, 주소의 복사)

  • 주소의 복사, 실제 데이터가 존재하는 주소를 가리키는 주소값을 인자로 넘겨서 매개변수로 전달한다.
  • 복사하지 않고 직접 참조하기에 빠르다.
  • 직접 참조를 하기에 원래 값이 영향을 받는다(리스크).
  • 객체, 배열은 주소가 복사된 것이기 때문에 복사본을 수정하면 원본도 같이 바뀐다.

얕은 복사와 깊은 복사는 객체를 복사할 때 사용되는 개념이다.

얕은 복사(shallow copy)

let obj1 = { a: 1, b: 2 };
let obj2 = obj1;  // 얕은 복사

obj2.a = 3;
console.log(obj1.a); // 3
  • 객체를 복사할 때, 해당 객체의 참조(reference)만 복사하는 방식이다.
  • 원본 객체와 복사본 객체는 같은 객체를 참조하게 된다.
  • 따라서 복사본 객체를 수정하면 원본 객체도 같이 수정되는 부작용이 발생한다.

객체를 그대로 복사하여 사용하면 원본 객체의 데이터가 변경될 수 있기 때문에 객체의 값을 복사하고 사용하고 싶다면 얕은 복사가 아닌 깊은 복사를 사용하는 것이 좋다.

깊은 복사(deep copy)

  • 깊은 복사는 객체를 완전히 복사하는 방식이다.
  • 원본 객체와 복사본 객체는 서로 독립적으로 존재한다.
  • 복사본 객체를 수정해도 원본 객체에는 영향을 주지 않는다.

자바스크립트에서 객체의 깊은 복사 방법

1.Object.assign() 함수 사용

let obj1 = { a: 1, b: 2 };
let obj2 = Object.assign({}, obj1);  // 깊은 복사

obj2.a = 3;
console.log(obj1.a); // 1 -> 원본값 보존됨
console.log(obj2.a); // 3

1차원 객체는 원본값이 보존되는 깊은 복사가 이루어졌다
하지만 2차원 객체를 복사할 때는 깊은 복사가 이루어지지 않는다

const obj = {
  a: 1,
  b: { c: 2 },
}

const copyObj = Object.assign({}, obj);

copyObj.b.c = 3;

console.log(obj); // { a: 1, b: { c: 3 } } -> 오잉 원본값이 변경됐다
console.log(obj.b.c === copyObj.b.c); // true

이는 복사된 하위 객체 {c: 2}도 결국 객체이기 때문에 얕은 복사가 된 것이다.
그래서 Object.assign() 메서드는 1차원 객체만 깊은 복사 해야한다.

2.전개 연산자(Spread Operator)

const obj = {
  a: 1,
  b: { c: 2 },
};

const copyObj = {...obj};

copyObj.b.c = 3;

console.log(obj); // { a: 1, b: { c: 3 } }
console.log(obj.b.c === copyObj.b.c); // true

전개 연산자도 Object.assign()와 마찬가지로 2차원 객체는 얕은 복사가 된다.

3. JSON의 stringify()와 parse()

  • 객체를 문자열로 만들고 다시 객체로 만든다. (모두 새 주소를 갖게 된다.)
  • stringify(): JSON.stringify() 메소드는 객체를 인수로 받으며 받은 객체는 문자열로 치환한다.
  • parse(): JSON.parse() 메소드는 문자열을 인수로 받으며 받은 문자열은 객체로 치환한다.
  • 이 방법은 2차원 객체도 깊은 복사가 되지만 다른 방법에 비해 성능이 느려서 lodash라는 라이브러리를 사용한다.

4. lodash 라이브러리의 cloneDeep()

  • lodash 라이브러리의 cloneDeep() 메소드를 이용하면 깊은 복사를 간편하게 구현할 수 있다.
const obj = {
  a: 1,
  b: { c: 2 },
  func: function () {
    return this.a;
  }
};

const copyObj = lodash.cloneDeep(obj);

copyObj.b.c = 3;
console.log(obj); // { a: 1, b: { c: 2 }, func: [Function: func] }
console.log(obj.b.c === copyObj.b.c); // false
profile
지금은 개발 모드

0개의 댓글