[자바스크립트] 평가 전략 - Call By Value vs Call By Reference

jimmyjoo·2020년 11월 2일
5

자바스크립트

목록 보기
9/11
post-thumbnail

평가 전략(Evaluation Strategy)📍

(평가전략?? 그게 뭐야...?) 🤨

프로그래밍을 공부하다보면 "Call-By-" 또는 "Pass-By-"로 시작하는 용어들을 접한다. 그런 용어들이 의미하는 것이 바로 평가 전략이다.

Wikipedia에 따르면

평가전략이란 프로그래밍 언어에서 함수 호출의 아규먼트(argument)의 순서를 언제 결정하고 함수에 어떤 종류의 값을 통과시킬지 결정하는 것이다.

(참고로 "Call-By-"와 "Pass-By-"는 동의어처럼 쓰인다.
차이점은 "Call"은 함수를 "호출"할 때 사용하는 동사이고
"Pass"는 인자를 "전달"한다는 의미를 나타내는 동사이다.
즉, 바라보는 기준이 함수냐 인자냐에 따라 사용하는 동사가 다른 것고 큰 틀에서 같은 의미이니 어떤 것을 써야하나 크게 고민하지 않아도 될 것 같다.)

이 글에서는 대표적인 평가 전략인 Call by Value와 Call By Reference에 대해 학습하고 자바스크립트는 어떤 평가 전략을 사용하는지 알아보자.


Call By Value📍

먼저 값에 의한 전달에 대한 특징부터 살펴보자.

1. argument로 값이 넘어온다.
2. 값이 넘어올 때 복사된 값이 넘어온다.
3. caller(호출하는 녀석)가 인자를 복사해서 넘겨줬으므로 callee(호출당한 녀석)에서 해당 인자를 지지고 볶아도 caller는 영향을 받지 않는다.

let a = 1;

function addOne(b) { //callee
  b = b + 1;
}

addOne(a); //caller

console.log(a); // 1

👉 a라는 변수를 인수로 넘겨주었다. 이때 1이라는 값은 복사되어 인자b에게 할당된다. ab의 값은 같지만 둘 다 다른 메모리 공간을 차지하게 되어 별개의 존재이기 때문에 함수 내부에서 b를 지지고 볶아도 a한테는 아무런 영향이 없다.


Call By Reference📍

이번에는 Call By Reference에 대한 특징을 살펴보자.

1. arguments로 reference(값에 대한 참조 주소, 메모리 주소를 담고있는 변수)를 넘겨준다.
2. reference를 넘기다 보니 해당 reference가 가리기는 값을 복사하지는 않는다.
3. caller(호출하는 녀석)가 인자를 복사해서 넘기지 않았으므로 callee(호출당한 녀석)에서 해당 인자를 지지고 볶으면 caller는 영향을 받는다.

아례 예시를 살펴보자.

const me = {
  name: 'Jimmy'
};

function changeName(person) { //callee
  person.name = 'Joo';
}

console.log(me); // { name: 'Jimmy' }

changeName(me); //caller

console.log(me); // { name: 'Joo' }

👉 매개변수 person에 인수로 넘겨진 me의 참조값이 전달되었다. 따라서 meperson는 같은 참조값을 가지고 있다. 따라서, changeName 함수 안에서 person.name을 바꾸어 주면 me.name도 변하는 것을 확인할 수 있다.

👉 아~ 그러면 참조타입을 인수로 넘겨주고 함수 안에서 인자를 지지고 볶고 했을때 인자가 변할 수 있구나! 그리고 이런 경우 Call By Reference라고 하는구나!

나도 딱 이렇게 생각했다...
근데 다음 예시를 보니 멘붕이 왔다..🤯


또 다른 예시📍

const me = {
  name: 'Jimmy'
};

function changeName(person) { //callee
  person = { name: 'Joo' };
}

console.log(me); // { name: 'Jimmy' }

changeName(me); //caller

console.log(me); // 정답은??

👉 정답은 뭘까?

음.. 함수에 참조타입을 인수로 넘겨주었고 함수 내에서 인자를 지지고 볶고(새로운 객체를 할당) 했으니까 함수 외부에 있는 객체(me)도 변했겠지?
참조타입은 Call By Reference이니까!
그렇다면 정답은 { name: 'Joo' }!
(창피하지만 나는 자신있게 이렇게 생각했다...😅)

응 아니야~👎

정답은 { name: 'Jimmy' }이다. 아무런 변화가 없다.

WHAT?!

🤔📍

왜 그런걸까? 무엇이 잘못된걸까?

방금 위에 있던 예시 코드를 그림을 그려가며 따라가보면 왜 저런 결과가 나왔는지 이해하기 쉽다.

먼저

const me = { name: 'Jimmy'};

객체를 만들고 me라는 변수에 할당하였다. 이를 간단한 그림으로 표현하자면 아래와 같다. (박스 위에 빨간색으로 적힌 16진수가 메모리 주소다.) 참조타입이기 때문에 me라는 변수는 객체의 참조값을 가지고 있다.


다음으로 함수를 정의하고 함수를 호출하였다.

function changeName(person) { //callee
  person = { name: 'Joo' };
}

changeName(me); 

호출하고 인수가 넘어가는 부분부터 살펴보자. 인수로 me객체를 넘겨주었다. 이때 정확하게 넘고 짚어가야할 부분은 me를 인수로 넘겼지만 사실상 참조값이 복사되어 넘어간다. 따라서 인수(매개변수)인 person은 복사된 참조값을 가지고있고 같은 객체를 가리키고 있다. 이는 아래 그림과 같다.


마지막으로 함수 내부가 실행된다.
person = { name: 'Joo'}

위 코드가 실행되어 새로운 객체가 인수인 person한테 할당된다.
이를 아래 그림을 통해 살펴보자. 먼저 새로운 객체({name: 'Joo'})가 생성된다. 그 후에 person에게 할당된다. 이를 통해 person이 원래 가지고 있던 me의 참조값(0x111)이 새로운 객체의 참조값(0x222)로 바뀌게 된다. 즉, meperson은 이제 다른 객체를 참조하고 있다.

즉, 결국 자바스크립트는 Call By Value였다. Call By Reference라고 생각했던 부분은 단지 인수로 넘어갔던 값이 참조타입이였고 함수 내부에서 지지고 볶으면 외부에도 변화가 생기기 때문에 naive하게 "참조타입은 Call By Reference"라고 단정지어 버렸다.


결론📍

사람들은 주로 원시타입은 Call By Value, 참조타입은 Call By Reference라고 표현한다. 이러한 표현은 값의 종류가 원시타입인지 참조타입인지를 구별하여 강조하는 의미에서 부르는 표현이다.

하지만, 정확하게 말하자면 틀린 표현이다.
원시타입은 Call By Value! 참조타입은 Call By Value of Reference!
이 표현이 더 맞지 않을까?

결국, 변수가 가리키는 메모리 공간에 저장되어 있는 값을 복사하여 전달한다는 관점에서 바라볼 때 자바스크립트는 항상 값에 의한 전달(Call By Value)만 존재한다고 말할 수 있다.

값이 원시값이냐 참조값이냐의 차이만 있을 뿐이다. 원시 타입의 경우 값이 복사되어 전달되고 참조 타입의 경우 메모리 주소의 값(참조값)이 복사되어서 전달되기 때문에 두 경우 모두 동작 원리는 같다.




참고 👏

profile
Front-End Newbie 🧐

0개의 댓글