JS - call by value VS call by Reference

July 06, 2020

call by value VS call by Reference

다른 언어에서 값은 구문에 따라 call by value 나 call by ref로 전달, 할당 된다.

c++에서는 &와 같은 참조 연산자 등을 이용해 레퍼런스를 넘긴다. Alias 이름을 가지는 것이다. 또한 배열등을 제외하고는 참조 연산자나 포인터 연산자를 사용하지 않으면 항상 call by value로 전달이 된다.

이에 비해 자바스크립트는 참조연산자나 포인터등 직접 변수의 메모리의 주소에 별명을 붙히는 것을 지원하는 것이 없다. 또한 참조방법도 다르며 다른 변수가 다른 변수를 참조 할 수 없다.

자바스크립트에서는 reference는 공유된 값을 가리킨다.(다른 언어들이 변수의 공간을 가리키는 것과 다르다.) 따라서 서로다른 10개의 래퍼런스가 있다면 저마다 항상 공유된 값을 참조한다. 값의 타입만으로 copy by value 나 copy by ref가 결정이 되는 것이다.

//val는 값을 복사한다.
let a=3;
let b=a;
// 'b'는 값을  a 에서 값을 복사한다  

b++;
a;//2
b;//3

//array는 값의 ref가 전달 된다.
let c=[1,2,3];
let d=c;

d.push(4);

c===d;//true

null, undefined string, number, boolean, symbol은 항상 값을 복사한다.

Object나 함수, 배열, 등은 할당/전달 시에 ref를 전달 한다.

c,b는 값을 가지고 있는 것이 아니라, 동등하게 값이 위치하는 ref를 참조하고 있는 것이다.

레퍼런스는 변수가 아니라 값 자체를 가리키는 것이기 때문에 A레퍼런스의 참조 대상을 바꿈으로써 B를 바꿀 수있는 포인터와 유사한 방법은 지원하지 않는다. 즉

c=[1,2,4,1,1212,];

c!==d//true

가 되는 것이다. c에 새로운 배열을 할당하면 새로운 배열의 위치를 참조하기 때문이다.

함수 인자도 값의 레퍼런스로 전달이 되는데, 포인터가 아니어서 혼돈되는 부분이 있다.

function foo(x){
    x.push(4);
    x;//[1,2,3,4];

    x=[4,5,6]//참조하는 값이 바뀜
    x.push(7);
    x;
}

let a=[1,2,3];
foo(a);
a;//[1,2,3,4]????????????????????????????

이는 x가 전달 될 때 a 에 들어가 있는 “값”의 ref가 전달되었기 때문이다. 그래서 x.push(4) 를 통해서는 x에서 참조 하고있는 전달된 원래 배열을 변화 시키지만 새로운 배열을 할당함으로써 이전에 가리키고 있던 배열의 ref를 더이상 가리키지 않기 때문에 반영이 되지 않는다.

따라서 위의 함수를 일반적으로 기대하는 형태로 작동하게 하기 위해서는 다음과 같이 처리 해야 한다.

function foo(x){
    x.push(4);
    x;//[1,2,3,4];

    x.length=0;
    x.push(4,5,6,7);//원본 배열을 수정
    x;//[4,5,6,7]
}

let a=[1,2,3];
foo(a);
a//[4,5,6,7]

우리는 call by ref, call by val을 선택 할 수 없음을 기억 해야 한다.

또한 만약 기존 변수의 값을 바꾸고싶지 않다면 다음과 같이

foo(a.slice());
foo([...a]);

등과 같이 새로운 배열을 얕은복사로 반환하는 값을 사용해야한다. (얕은 복사: 값을 복사하긴 하되 만약 복사되는 객체의 하위에 객체나 배열이 있다면 그 값은 ref로 복사가 되는 것을 뜻한다.)

반대로 스칼라 값을 ref로 전달 하려고 하면 배열이나 객체, 함수 등으로 감싸야 한다.


Written by Juyeong Byeong . github