Frontend/JS

자바스크립트 - Call by value/ Call by reference/ Call by sharing

Creative_Lee 2022. 2. 16. 17:14

☠ 과연 정확히 알고 있는가? ☠

 

구글 검색창에 Call by value, Call by reference를 검색하면 많은 자료들이

 

Call by value

  • 함수 호출시 전달받은 인자가 원시 타입이면?
    원본 값을 그대로 복사하여 매개변수로 전달합니다!
  • 원본 값이 함수의 영향 받지 않습니다!

Call by reference

  • 함수 호출시 전달받은 인자가 참조 타입이면?
    인자의 메모리 주소값(참조)을 매개변수로 전달합니다!
  • 원본 값이 함수의 영향을 받습니다!

 

라고 설명하고 있습니다.

 

 

처음 Call by XXX 키워드를 검색해서 공부할 때
원시 타입, 참조 타입에 대한 값 복사 시 각각 어떤 차이가 있는지에 대해 이미 공부한 뒤여서

비슷한 내용이라고 생각했고 그다지 어렵지 않다고 생각해 차이점만 인지하고 넘어갔었습니다.

아무생각 없이 모든 언어에서 통용되는 개념인줄 알았구요!

 

하하.. 대충 공부한 것에 대한 대가는 컸습니다!

 

 

이 포스팅의 결론부터 말하자면....

JavaScript에는 Call by reference 개념이 존재하지 않습니다!
대신 Call by sharing 개념이 존재합니다!

 


1. Call by value

인자를 새 메모리 영역에 복사하여 매개 변수에 전달한다.

caller로 전달한 인자의 원본값은 함수 실행 후에도 변하지 않는다.

알고 있던 개념과 다르지 않습니다.

 

 


2. Call By reference

인자의 참조(메모리 주소)를 매개 변수에 전달한다.

caller로 전달한 인자의 원본값이 함수 실행 후에 수정될 수 있다.

역시 알고 있던 개념과 다르지 않습니다.

 


3. JS 코드에서의 증명

이제 코드를 작성하면서 실제로 확인해 보겠습니다!

function change(value , ref1 , ref2){
  value += value
  ref1.name = 'changed'
  ref2 = { name : 'changed' }
}

var number = 1;
var object1 = { name : 'change me plz'};
var object2 = { name : 'change me plz'};

change(number,object1,object2)

console.log(number) // 1
console.log(object1) // {name: 'changed'}
console.log(object2) // {name: 'change me plz'}

number는 변경되지 않았고, object1은 변경되었습니다. 아주 정상적입니다.

하지만 object2 는 변경되지 않았습니다.

 

분명 Caller에서 객체를 인자로 넘겼습니다.

객체는 참조 타입입니다.

 

만약

Call by value로 동작했다면 object1 은 변경되지 않았어야 합니다.

Call by reference로 동작했다면 object2 는 변경되었어야 합니다.

 

둘다 만족하지 못하는 결과입니다.

 

이 상황을 Call by sharing으로 설명할 수 있습니다.


4. Call by sharing 

 

4-1. 개념

자바, 자바스크립트, 파이썬, 루비 등등의 언어에서 Call by sharing을 사용합니다.

call by sharing이라는 명칭은 보통 잘 쓰이지 않습니다.
용어가 여러 방면에 걸쳐 일관성이 없습니다.
(의역하면 call by value와 call by reference 사이에서 애매한가 봅니다. 
예시로 자바에선 call by value 라고 부른다고 나옵니다.)

 

우선 이것은 Call by value가 아닙니다.
호출된 함수에 의한 인자의 변화가 호출자에게 보이기 때문입니다.

객체가 복사되거나 복제되지 않으므로 호출자는 함수의 의한 객체의 변화를 볼 수 있습니다.

 

또한

 

이것은 Call by reference가 아닙니다.

호출시 전달한 인자에 대한 접근 권한이 주어지지 않고,

호출된 함수 내에서 인자에 대한 할당을 할 수 없습니다.  

 


 

4-2. 주소의 관점에서 코드 다시보기

Call by sharing의 개념을 참고해서 다시 코드를 봐봅시다.  

 

 

1. 변수를 선언하고 객체를 할당하면 

 

  각 변수는 객체의 메모리주소 값( 0x100, 0x200 )을 가지고 있다. ( 참조 타입 ) 

 

 

2. 함수를 정의 하고 호출한다.

 

 

3. 각 객체가 함수의 인자로 매개변수에 전달된다.

JS에서는 사실 참조값을 바로 전달하는게 아닙니다.
           참조값에 대한 복사본을 만들어서 전달합니다.

JS에는 참조값 자체를 전달할 수 있는 방법이 없습니다.

C, C++ 과 같은 언어처럼 포인터나 주소값을 뽑아내는 연산자도 없습니다.

 

언어 내부적으로 참조 타입을 인자로 전달하여도 복사본을 만들어 전달하도록 설계되어 있습니다.

 

 

 

4. 함수 내부 동작이 실행된다.

   ref1은 object1 이 가지고 있던 객체의 주소값을 복사해서 가지고 있습니다.. 

   그러므로 함수 내부에서 '0x100' 메모리 주소로 프로퍼티에 접근해 값을 변경할 수 있습니다. 

 

   -->  인자로 전달된 object1 객체가 함수에 의해 수정되었습니다.

 

   ref2은 object2 가 가지고 있던 객체의 주소값을 복사해서 가지고 있습니다.. (아직 0x200을 가지고 있습니다.)

   새로운 객체 {name : 'changed'}를 만들고 ref2에 할당했습니다.  

   ref2는 이제 새로운 객체의 메모리 주소 '0x300'을 가지고 있습니다.

   

   -->  인자로 전달된 object2 객체는 수정되지 않았습니다.

 


5. JS는 Call by sharing이다! 하지만 Call by value다..?

 

결국 JavaScript에는 Call by reference가 존재 하지 않고( 메모리 직접 핸들링 불가능 )
참조 타입 인자도 복사해서 전달하기 때문에 Call by value만 존재한다고도 할 수 있습니다.

 

하지만 또 함수 내부에서 전달된 인자의 수정이 가능하기 때문에...
Call by value도 아닌것이 Call by reference도 아닌


Call by sharing라는 사실을 알 수 있습니다...

 


 

번외. 근본 Call By reference 

 

문서를 몇 줄 더 읽어보면 Call by reference의 근본에 대해 나옵니다.

 

Call by value를 사용하고 Call by reference를 일부 지원하는 언어에서

포인터와 같은 참조를 사용하여 Call by reference를 시뮬레이션 해볼 수 있다.

이것은 별개의 평가 전략이 아닌, 그저 Call by value다!


(simulate의 의미가 ,, ~인 척 하다, ~ 처럼 보이게 만들어지다. 라는 뜻도 있어서 애매하네요) 

 

순수 함수형 언어에서는 일반적으로 Call by reference와 Call by value의 의미적 차이가 없다.

그래서 내부적으로 Call by reference를 사용하더라도 Call by value로 기술된다.

 

 

여러 언어에서 뜨거운 감자 느낌인데,,, 여튼 뭐..

예... 그렇다고 하네요.. 

 

저는 확실하게 알았으니까요!