call by value
와 call by reference
란 변수나 객체등이 함수의 인자(argument)로 들어와 매개변수(parameter)로 전달될 때 어떤 방식으로 전달될 지를 결정하는 방식이다.
일반적으로 기본형(원시형)을 매개변수로 넘길 때는 Call by value 방식으로 넘기고
참조형을 매개변수로 넘길 때는 Call by reference 방식으로 넘긴다.
기본형(원시형) : 숫자, 문자열, 불리언, null, undefiend, 심볼
참조형 : 객체, 배열, 함수, 날짜, 정규표현식
인자(argument)는 어떤 함수가 호출될 때 전달되는 값을 의미하고
매개변수(parameter)는 전달된 값을 받아들이는 변수를 의미한다.
function sum(a, b) { // a, b는 매개변수(parameter)
return a + b;
}
sum(10, 20); // 10, 20은 인자(argument)
Call by value(값의 복사)는 말 그대로 복사된 값을 인자로 넘겨서 매개변수로 전달한다.
기본형(Primitive type)의 경우 call by value 방식으로 전달된다.
call by reference(주소값의 복사)는 실제 데이터가 존재하는 주소를 가리키는 주소값을 인자로 넘겨서 매개변수로 전달한다.
객체(ex. obj, array, function ...)는 call by reference 방식으로 전달된다.
원시타입의 경우 call by value로 동작한다.
참조타입의 경우 call by reference로 동작한다.
JavaScript는 어떤 타입의 자료형(기본형, 참조형)이던지 항상 call by value를 사용한다.
참조형 타입이여도 참조형의 주소값을 복사해서 매개변수로 전달한다.
function foo(obj2) {
obj2= 10;
console.log(obj2); // 10
}
let obj1 = { a: 5, b: 8 };
foo(obj1);
console.log(obj1); // { a: 5, b: 8 }
포인터와 call by reference 개념을 가진 C, C++ 언어의 경우에는 2번째 줄의 obj2 = 10;
에 의해서 obj1
의 값이 10으로 바뀐다. 왜냐하면 완전한 주소값 참조로 연결되어 있기 때문이다.
하지만 JS의 경우에는 객체의 프로퍼티 값이 아닌 객체 자체의 변경을 허용하지 않는다. 왜냐하면 겉보기에는 주소값을 참조하는 것 같지만 실제로는 주소값의 복사본을 만들어서 넘기기 때문이다.
먼저 foo(obj1);
에서 obj1
이 함수 foo()
의 인자로 넘어가서 obj2
라는 매개변수로 전달될 때, obj1
이 가리키고 있는 주소값과 동일한 주소값을 가진 obj2
가 복사되서 전달된다.
그 다음 함수 내부의 obj2 = 10;
에서 obj2
의 주소값이 10
이 저장된 위치의 주소값으로 변경된다.
const f1 = obj => {
obj = null;
}
let b1 = {'a': 1};
f1(b1);
console.log(b1); // { a: 1 }
b1이라는 객체를 null로 만드는 것 같지만 그러게 되지 않는다.
obj에는 주솟값의 복사본이 들어가게 되고,
우리가 원하는 b1 주솟값에 값이 할당된느 것이 아니라 또 다른 주솟값에 null이 할당된다.
const f2 = array => {
array = null;
}
let b2 = [];
let a2 = b2;
f2(b2);
console.log(b2); // []
배열도 case1과 마찬가지이다.
const swap = (a, b) => {
let tmp = a;
a = b;
b = tmp
}
let x = 1;
let y = 2;
swap(x, y);
console.log(x, y); // 1, 2
기본형을 넘겨도 마차가지이다. 스왑이 일어나지 않는다.
const f = (a) => {
a = 2;
}
let b = 11;
f(b);
console.log(b); // 11
case3과 비슷하다. 기본형을 넘겨도 복사된 주소를 갖고 있는 변수에서 값을 변경했기 때문에 출력은 11이다.
const a = { 'a': 1 };
const b = (c) => {
c.a = 2;
}
b(a);
console.log(a) // { a: 2 }
객체의 속성은 변하게 된다.
참조형의 복사본인 객체를 전달했고 이 객체는 다른 주솟값을 갖지만 둘 다 똑같은 { 'a' : 1 }
이라는 값을 바라보고 있기 때문이다.
이 상태에서 c의 속성이 변화하면 똑같은 값을 바라보고 있는 a의 속성도 변하게 된다.
const foo = (b) => {
b = b.a = 1;
b.b = 2;
};
const a = {};
foo(a);
console.log(a); // { a: 1 }
2번째 줄 b = b.a = 1;
을 보자.
할당 연산자를 여러 개 연결한 경우, 평가는 우측부터 진행된다. (참고 - 할당연산자 체이닝)
b.a = 1
로 인해 변수 a가 가리키는 값에 { a: 1 }
가 할당된다.
그리고 다시 b에는 1이 할당된다.
3번째 줄 b.b = 2;
는 기본형에 프로퍼티를 할당했기 때문에 무시가 된다.
따라서 결과적으로 a는 { a: 1 }
이 된다.