Call by Value & Call by Reference

결론

Call by Value는 원시 값을 갖는 변수를 다른 변수에 할당하면 원본의 원시 값이 복사되어 전달되는 것을 의미합니다.
Call by Reference는 객체를 가리키는 변수를 다른 변수에 할당하면 원본의 참조 값이 복사되어 전달되는 것을 의미합니다.

값에 의한 전달과 참조에 의한 전달을 ‘pass’를 사용해 표현하고, 함수를 호출하면서 매개변수에 값을 전달하는 방식을 ‘call’을 사용해 표현하여 둘을 구분하기도 하지만 동작 방식이 동일하므로 이 글에서는 동의어라고 가정하고 설명하겠습니다.

설명

값에 의한 전달 (call by value = pass by value)

var score = 80

var copy = score

console.log(score, copy) // 80 80
console.log(score === copy) // true

score = 100

console.log(score, copy) // 100 80
console.log(score === copy) // false

score 변수와 copy 변수의 값 80은 다른 메모리 공간에 저장된 별개의 값입니다. 따라서 score 변수의 값을 변경해도 copy 변수의 값에는 어떠한 영향도 주지 않습니다.

참조에 의한 전달 (call by reference = pass by reference)

var person = {
  name: 'Lee
}

// 참조 값을 복사(얕은 복사)
var copy = person;

console.log(copy === person); // true

copy.name = 'Kim'

person.address='Seoul';

console.log(person) // {name: "Kim", address: "Seoul"}
console.log(copy) // {name: "Kim", address: "Seoul"}

원본 person을 사본 copy에 할당하면 원본 person의 참조 값을 복사해서 copy에 저장합니다. 이때 person과 copy는 저장된 메모리 주소는 다르지만 동일한 참조 값을 가집니다. 이것은 두 개의 식별자가 하나의 객체를 공유한다는 것을 의미합니다.

결국 “값에 의한 전달”과 “참조에 의한 전달”은 식별자가 기억하는 메모리 공간에 저장되어 있는 값을 복사해서 전달한다는 면에서 동일합니다.
다만 식별자가 기억하는 메모리 공간, 즉 변수에 저장되어 있는 값이 원시 값이냐 참조 값이냐의 차이만 있을 뿐입니다. 따라서 자바스크립트에는 “값에 의한 전달”만이 존재한다고 말할 수 있습니다.
(포인터가 존재하는 다른 프로그래밍 언어의 참조에 의한 전달과 의미가 정확히 일치하지 않습니다.)
그런 이유로 두 용어 다 쓰지 않고 “공유에 의한 전달(pass by sharing)“이라고만 표현하는 경우도 있습니다.

“값에 의한 전달”이라는 용어에도 오해가 있을 수 있습니다. 엄격하게 표현하면 변수에는 값이 전달되는 것이 아니라 메모리 주소가 전달되기 때문입니다. 단, 전달된 메모리 주소를 통해 메모리 공간에 접근하면 값을 참조할 수 있습니다.

값의 비교

var person1 = {
  name: 'Lee',
}

var person2 = {
  name: 'Lee',
}

console.log(person1 === person2) // false (두 변수는 서로 다른 메모리에 저장된 별개의 객체를 가리킨다.)
console.log(person1.name === person2.name) // true (두 표현식 모두 원시 값 'Lee'로 평가된다.)

=== 일치 비교 연산자는 변수에 저장되어 있는 값을 타입 변환하지 않고 비교합니다.

객체를 할당한 변수는 참조 값을 가지고 있고, 원시 값을 할당한 변수는 원시 값 자체를 가지고 있습니다. 따라서 === 일치 비교 연산자를 통해 객체를 할당한 변수를 비교하면 참조 값을 비교하고, 원시 값을 할당한 변수를 비교하면 원시 값을 비교합니다.

함수를 호출하면서 매개변수에 값을 전달하는 방식

앞에서 설명한 원시 값 갖는 변수를 다른 변수에 할당하는 것을 시 값에 의한 전달(pass by value), 객체 가리키는 변수를 다른 변수에 할당하는 것을 참조에 의한 전달(pass by reference)이라고 하고,
함수를 호출하면서 매개변수에 값을 전달하는 방식을 값에 의한 호출(call by value), 참조에 의한 호출(call by reference)라고 구별해 부르기도 합니다.

그러나 동작 방식은 동일합니다.

function changeVal(primitive, obj) {
  primitive += 100 // primitive는 원시 값으로, 변경 불가능한 값이므로 재할당을 통해 할당된 원시 값을 새로운 원시 값으로 교체했다.
  obj.name = 'kim' // obj는 객체로, 변경 가능한 값이므로 직접 변경할 수 있기 때문에 재할당 없이 직접 할당된 객체를 변경했다.
}

var num = 100
var person = { name: 'Lee' }

console.log(num) // 100
console.log(person) // {name: 'Lee'}

// 원시 값은 값 자체가 복사되어 전달되고, 객체는 참조 값이 복사되어 전달된다.
changeVal(num, person)

console.log(num) // 100
console.log(person) // {name: 'Kim'}

원시 타입 인수는 값 자체가 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 그 값을 변경(재할당을 통한 교체)해도 원본은 훼손되지 않습니다. 다시 말해, 외부 상태, 즉 함수 외부에서 함수 몸체 내부로 전달한 원시 값의 원본을 변경하는 어떠한 부수 효과도 발생하지 않습니다.

하지만 객체 타입 인수는 참조 값이 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 참조 값을 통해 객체를 변경할 경우 원본이 훼손됩니다. 다시 말해, 외부 상태, 즉 함수 외부에서 함수 몸체 내부로 전달한 참조 값에 의해 원본 객체가 변경되는 부수 효과가 발생합니다.

자바스크립트에는 “값에 의한 전달”만이 존재

function changeVal(primitive, obj) {
  primitive += 100
  obj = { hi: 'hi' }
}

var num = 100
var person = { name: 'Lee' }

console.log(num) // 100
console.log(person) // { name: 'Lee' }

changeVal(num, person)

console.log(num) // 100
console.log(person) // // { name: 'Lee' }

위에서 말했던 값에 의한 전달과 참조에 의한 전달 모두 결국 값을 복사해서 전달한다는 면에서 동일하다는 것을 이것을 통해 확인할 수 있습니다.

함수의 인자 ojb에 넘겨진 것은 참조 “값”입니다.
함수가 호출되어 렉시컬 환경이 만들어질 때 매개변수 obj 변수에 객체의 참조 값을 담아 등록할 뿐입니다. (obj: 참조 값)
그렇기 때문에 새로운 객체의 참조 값을 할당할 시 person의 참조 값은 잊어버리므로 person은 아무런 영향을 받지 않게 됩니다.

추가 학습

참고

  • 모던 자바스크립트 Deep Dive

Written by정선아
🌱 공부한 것을 기록하여 성장하기 위한 블로그입니다.

GitHubGmail