자바스크립트의 객체는 call by reference가 아니다?

sudyn·2023년 7월 31일

JavaScript

목록 보기
3/14

자바스크립트의 원시타입과 참조타입을 다루며 call by value와 call by reference를 가볍게 다뤄보았다.
복사참조는 값을 변수로 할당할 때 값이 이동하는 과정에서 발생하는 매커니즘이다.

값에 의한 복사

자바스크립트의 기본(원시)타입은 실제 값 자체가 해당 변수에 복사된다.
값 자체가 변수에 저장되므로 변수 간에 독립적인 값이 유지된다.

코드를 실행하면서 어떤 값이 저장되는지 살펴보자.

let message = 'Hello!'; (1)
let pharse = message;   (2)

pharse = 'Bye!';        (3)

(1) message 변수에 Hello! 문자열을 할당

message‘Hello!’

(2) message의 값을 pharse에 복사!

message‘Hello!’
pharse‘Hello!’

그렇다면 message와 pharse의 값은 ‘Hello!’는 같은 값일까?

(3)라인을 실행하게 되면 ? pharse만 'Bye!'으로 변경된다.

message‘Hello!’
pharse'Bye!'

‘Hello!’가 message 값에 저장되면서 pharse로 값을 옮길때 = 즉 값이 복사된다.
최초의 message의 값과 pharse의 값은 관계가 없다.

pharse의 값을 변경하더라도 message의 원본값이 유지된다 → 불변성

Call by value

  • 함수 호출 시, 전달되는 인자의 값을 복사하여 매개변수로 전달하는 방식이다.
function increment(num) {
  num++;
  return num;
}

let value = 10;
let result = increment(value);

console.log(value); // 10
console.log(result); // 11 

increment 함수는 매개변수 num을 받아 해당 값을 증가시킨 후 반환한다.

그러나 원본 변수 value는 함수 호출 이후에도 변하지 않는다. 이는 전달된 인자가 복사된 값이기 때문에 함수 내에서 변경해도 원본 변수에는 영향을 주지 않기 때문이다.

즉, 값 자체가 메모리에 저장되기 때문에 Call by Value로 전달된다!

그렇다면? 기본형이 아닌 값은 어떻게 될까?

참조에 의한 객체 복사

참조 타입 변수에 값이 할당될 때, 메모리 주소(참조)가 변수에 복사된다.

객체는 메모리 내 어딘가에 저장되고, 변수 user엔 객체를 '참조’할 수 있는 값이 저장된다.

let user = { name: "sujin" };  (1)

let admin = user;              (2)

admin.name = "sejun";          (3)

console.log(user.name);        (4)
console.log(admin.name);       (5)
  • 변수는 해당 객체를 직접적으로 저장하는 것이 아니라, 객체의 위치를 가리키는 역할을 합니다.
  • 객체는 대입문을 통해서는 복사되지 않는다. 하나만 존재한다! 따라서 객체에 접근하거나 객체를 조작할 땐 여러 변수를 사용할 수 있다.

(1)(2)

user {name}‘sujin’
admin {name}‘sujin’

(3) admin 객체의 참조값에 의해 변경이 된다.!

user {name}‘sejun’
admin {name}‘sejun’

(4),(5) 모두 sejun으로 출력된다.

  • 객체는 한 곳에 저장되어 있고 위치(=주소)값만 변수에 저장된다 => 불변성을 가지지 않는다!

call by reference

  • call by refernce(주소값의 복사)는 실제 데이터가 존재하는 주소를 가리키는 주소값을 인자로 넘겨서 매개변수로 전달한다는 의미이다.
let obj1 = {
  isLoading: false,
};

function foo(obj1) {
  obj1.isLoading = true;
	console.log(obj1.isLoading);
}

foo(obj1);
console.log(obj1.isLoading);

obj1 객체의 isLoading라는 프로퍼티에 초기값으로 false를 할당한다.

foo 함수는 obj1이라는 파라미터를 받으며, 내부에서 obj1.isLoading 값을 true로 변경한다.

자바스크립트에서 객체는 참조 타입이기 때문에 함수에 객체를 전달하면 객체의 프로퍼티를 직접 변경할 수 있다.

여기서 foo 함수의 인자 obj1은? 바깥의 객체 obj1을 가리킨다!!!

함수 호출전은? →

obj1 {isLoading}false

foo함수 안쪽으로 들어가게 되면

obj1 {isLoading}true

바깥으로 나가면 obj1은 true로 바뀌며, 참조 메커니즘이 발생한다.

💡 객체를 서랍장에 비유한다면 변수는 서랍장을 열 수 있는 열쇠라고 볼 수 있다.
원시 타입은 개별의 서랍과 열쇠를 관리하는 것이고,
객체 타입은 여러 열쇠(변수)로 하나의 서랍(객체)을 관리하는 것이라 할 수 있다.

그렇다면 자바스크립트에서 객체는 call by reference 방식으로 작동하나?

아니다!!!!!!!!!!!! JavaScript에는 Call by reference 개념이 존재하지 않는다?!?!?

Call by Sharing

자바스크립트의 객체는 call by reference가 아니라고?

아래 코드를 보자.

function foo(obj2) {
	obj2 = 8;
  console.log(obj2);
}

var obj = { a: 5 };

foo(obj);
console.log(obj);

foo(obj); console.log(obj); 어떤값이 찍힐까?

foo(obj);   // 8
console.log(obj);   // { a : 5}

만약, call by reference 였다면 같은 참조(메모리 주소)를 바라보므로 8이 찍힌다.

하지만 8, { a: 5 } 로 출력된다.

→ 객체의 속성을 수정할 때는 참조하지만 객체 자체를 수정할 때는 참조 관계가 깨진다!

→ js는 call by reference가 아니다!

  • "Call by sharing"은 "call by value"와 유사한 방식으로 동작하지만, 참조 데이터 타입(객체와 배열 등)의 경우에만 차이가 있다.

  • 함수에 인자를 전달할 때, 원시 데이터 타입의 경우에는 값이 복사되어 전달되지만,
    참조 데이터 타입의 경우에는 객체나 배열의 실제 값이 아니라 메모리상의 주소(참조)가 복사되어 전달된다. 따라서 함수 내부에서 객체 또는 배열의 값을 변경하면 원본 객체 또는 배열에도 영향을 미치게 된다.

  • 참조 데이터 타입은 공유되는 것처럼 동작하게 되어 값의 변경이 원본에 반영되므로 call by sharing 이라고도 표현한다.

  • 따라서, "Call by Reference" 대신에 "Call by Sharing"이라는 용어를 사용하는 것이 더 정확한 표현이다. 하지만 이도 옳지는 않다. js는 call by value라고 말하는게 옳다!

왜 알아야 하냐?

  • 함수 호출 시 인자로 전달되는 데이터의 동작 방식을 파악하여 예상치 못한 결과를 방지할 수 있다.
  • 불변성(Immutability)을 유지하려는 목적으로 객체를 다루거나 함수형 프로그래밍 시 도움이 된다.

📖 참고

https://ko.javascript.info/object-copy
https://think0wise.tistory.com/65
https://www.youtube.com/watch?v=-w-oJp6OVd4

profile
개발계발하는 프론트엔드 개발자🍠

0개의 댓글