2025. 6. 5. 09:00ㆍJavascript/Javascript
객체와 원시 타입의 근본적인 차이 중 하나는 원시 값의 경우 값 그대로 복사되는(pass-by-value) 반면, 객체는 참조에 의해 저장되고 복사된다는 것이다(pass-by-reference).
let message = "Hello!";
let phrase = message;
따라서 위 예시를 실행하면 아래의 그림과 같이 두 개의 독립된 변수에 각각 문자열 'Hello!'가 저장된다.
반면 객체의 동작 방식은 이와 다르다. 변수에는 객체가 그대로 저장되는 것이 아니라 객체가 저장되어 있는 메모리 주소인 객체에 대한 참조 값이 저장된다.
let user = {
name: "John"
};
객체는 메모리 내 어딘가에 저장되고 변수 user에는 객체를 참조할 수 있는 값이 저장되는 것이다. 따라서 객체가 할당된 변수를 복사할 때에는 객체의 참조 값이 복사되고 객체는 복사되지 않는다.
let user = { name: "John" };
let admin = user; // 참조값을 복사함
이렇게 하면 변수는 2개이지만 각 변수에는 동일한 객체에 대한 참조 값이 저장되는 것이다.
따라서 해당 객체에 접근하거나 객체를 조작할 때에는 여러 변수를 사용할 수 있다.
let user = { name: 'John' };
let admin = user;
admin.name = 'Pete'; // 'admin' 참조 값에 의해 변경됨
alert(user.name); // 'Pete'가 출력됨. 'user' 참조 값을 이용해 변경사항을 확인함
객체를 서랍장에 비유하면 변수는 서랍장을 열 수 있는 열쇠라고 할 수 있다. 서랍장은 하나, 그 서랍장을 열 수 있는 열쇠는 2개(user, admin)인데 그중 하나를 사용해 서랍장을 열어 정리한 뒤 또 다른 열시로 서랍장을 열면 정리된 내용을 볼 수 있는 이치와 같다.
참조에 의한 비교
객체를 비교하는 경우 동등 연산자(==)와 일치 연산자(===)는 동일하게 동작한다. 비교 시 피연산자인 두 객체가 동일한 객체인 경우 참(true)을 반환한다. 아래는 두 변수가 같은 객체를 참조하는 예시이다. 동등/일치 비교에서 모두 참이 반환된다.
let a = {};
let b = a; // 참조에 의한 복사
alert( a == b ); // true, 두 변수는 같은 객체를 참조.
alert( a === b ); // true
아래는 또 다른 예시이다. 두 객체 모두 비어 있다는 점에서 같아 보이지만 각각 별도 선언된 독립된 객체이므로 동등/일치 비교 시 거짓(false)이 반환된다.
let a = {};
let b = {}; // 독립된 두 객체
alert( a == b ); // false
이제 우리는 객체가 할당된 변수를 복사하면 동일한 객체에 대한 참조 값이 하나 더 만들어진다는 걸 알게 되었다. 그런데 객체를 복제하고 싶다면 어떻게 해야 할까? 기존에 존재하던 객체와 똑같으면서도 독립된 객체를 만들고 싶다면?
방법은 있지만 자바스크립트는 객체 복제에 대한 내장 함수를 지원하지 않기 때문에 약간의 어려움이 있다. 사실 객체를 복제해야 할 일은 그리 많지 않다. 참조에 의한 복사로 해결 가능한 일이 대다수일 뿐.
정말 복제가 필요한 상황이라면 아래와 같이 먼저 새로운 객체를 생성한 뒤 기존 객체의 프로퍼티들을 순회해 원시 수준까지 프로퍼티를 복사하면 된다.
let user = {
name: "John",
age: 30
};
let clone = {}; // 새로운 빈 객체
// 빈 객체에 user 프로퍼티 전부를 복사해 넣는다.
for (let key in user) {
clone[key] = user[key];
}
// 이제 clone은 완전히 독립적인 복제본이 되었다.
clone.name = "Pete"; // clone의 데이터를 변경.
alert( user.name ); // 기존 객체에는 여전히 John이 있다.
이 외에도 객체를 복제하기 위해서 Object.assign()이나 spread 연산자 등을 사용하는 방법도 있다. 이에 대한 자세한 내용은 아래 글을 참조하기로 하고 이 글에서는 객체의 참조에 대해 조금 더 살펴본 뒤 마무리하자.
[Javascript] 객체 복사 방법, 얕은 복사와 깊은 복사
예를 들어 아래와 같은 객체가 있다고 가정해 보자.const original = { num: 1000, bool: true, str: "test", func: function () { console.log("func"); }, obj: { x: 1, y: 2, }, arr: ["A", "B", "C"],}; 위 객체를 복제하여 새로운 변수
developing-move.tistory.com
Pass-by-reference
object type을 객체 타입 또는 참조 타입이라 한다. 참조 타입이란 객체의 모든 연산이 실제 값이 아닌 참조 값으로 처리됨을 의미한다. 원시 타입은 값이 한 번 정해지면 변경할 수 없지만(immutable) 객체는 프로퍼티의 변경, 추가, 삭제가 가능하므로 변경 가능한(mutable) 값이라 할 수 있다.
객체 타입은 동적으로 변화할 수 있으므로 어느 정도의 메모리 공간을 확보해야 하는지 예측할 수 없기 때문에 런타임에 메모리 공간을 확보하고 메모리의 힙 영역(Heap Segment)에 저장된다.
이에 반해 원시 타입은 값(value)로 전달된다(pass-by-value). 즉 값이 복사되어 전달된다는 것이다.
// Pass-by-reference
var foo = {
val: 10
}
var bar = foo;
console.log(foo.val, bar.val); // 10 10
console.log(foo === bar); // true
bar.val = 20;
console.log(foo.val, bar.val); // 20 20
console.log(foo === bar); // true
위 예시에서는 foo 객체를 객체 리터럴 방식으로 생성하였다. 이때 변수 foo는 객체 자체를 저장하고 있는 것이 아니라 생성된 객체의 참조 값, 즉 주소(address)를 저장하고 있다.
이후 변수 bar에 foo의 값을 할당하였다. 변수 foo의 값은 생성된 객체를 가리키는 참조 값이므로 변수 bar에도 같은 참조 값이 저장된다. 즉 변수 foo, bar 모두 동일한 객체를 참조하고 있다. 따라서 참조하고 있는 객체의 프로퍼티 val의 값이 변경되면 변수 foo, bar 모두 동일한 객체를 참조하고 있으므로 두 변수 모두 변경된 객체의 프로퍼티 값을 참조하게 된다. 객체는 참조(Reference) 방식으로 전달된다. 결코 복사되지 않는다.
그러나 아래의 경우는 위와 약간 차이가 있다.
var foo = { val: 10 };
var bar = { val: 10 };
console.log(foo.val, bar.val); // 10 10
console.log(foo === bar); // false
var baz = bar;
console.log(baz.val, bar.val); // 10 10
console.log(baz === bar); // true
변수 foo와 bar는 비록 내용은 같지만 별개의 객체를 생성하여 참조 값을 할당하였다. 따라서 foo와 bar의 참조 값은 동일하지 않다.
변수 baz에는 bar의 값을 할당하였다. 결국 baz와 bar는 동일한 객체를 가리키는 참조값을 저장하고 있다. 따라서 baz와 bar의 참조 값은 동일하다.
var a = {}, b = {}, c = {}; // a, b, c는 각각 다른 빈 객체를 참조
console.log(a === b, a === c, b === c); // false false false
a = b = c = {}; // a, b, c는 모두 같은 빈 객체를 참조
console.log(a === b, a === c, b === c); // true true true
Pass-by-value
원시 타입은 값(value)으로 전달된다. 즉 값이 복사되어 전달된다. 이를 pass-by-value(값에 의한 전달)라 한다. 원시 타입은 값이 한 번 정해지면 변경할 수 없다. 또한 이들 값은 런타임(변수 할당 시점)에 메모리의 스택 영역(Stack Segment)에 고정된 메모리 영역을 점유하고 저장된다.
// Pass-by-value
var a = 1;
var b = a;
console.log(a, b); // 1 1
console.log(a === b); // true
a = 10;
console.log(a, b); // 1 10
console.log(a === b); // false
위 예시에서 변수 a는 원시 타입인 숫자 타입 1을 저장하고 있다. 원시 타입의 경우 값이 복사되어 변수에 저장된다. 즉 참조 타입으로 저장되는 것이 아니라 값 자체가 저장되는 것이다. 변수 b에 a를 할당할 경우 변수 a의 값 1은 복사되어 변수 b에 저장된다.
'Javascript > Javascript' 카테고리의 다른 글
[Javascript] 전역 객체(Global Object) (0) | 2025.06.10 |
---|---|
[Javascript] 객체의 종류 (1) | 2025.06.09 |
[Javascript] 객체(Object) (1) | 2025.06.04 |
[Javascript] let, const 그리고 var (1) | 2025.06.02 |
[Javascipt] ECMAScript 2015(ES6) 그 이후 - (3) 클래스, 프로미스, 정규식 (1) | 2025.05.29 |