원시 값은 값에 의한 전달, 객체는 참조에 의한 전달방식으로 동작한다. 매개변수도 함수 몸체 내부에서 변수와 동일하게 취급되므로 매개변수 또한 타입에 따라 값에 의한 전달, 참조에 의한 전달 방식을 그대로 따른다.
함수를 호출하면서 매개벼누에 값을 전달하는 방식을 값에 의한 호출, 참조에 의한 호출로 구별해 부르는 경우도 있으나 동작 방식은 값에 의한 전달, 참조에 의한 전달과 동일하다.
function changeVal(primitive, obj) {
primitive += 100;
obj.name = 'lee';
}
let num = 100;
let person = { name: 'kim' };
console.log(primitive); // 100
console.log(person); // {name:'kim'}
changeVal(primitive);
console.log(primitive); // 100
console.log(person); // {name:'lee'}
changeVal 함수는 매개변수를 통해 전달받은 원시 타입 인수와 객체 타입 인수를 함수 몸체에서 변경한다. 더 엄밀히 말하자면 원시 타입 인수를 전달받은 매개변수 primitive의 경우, 원시 값은 변경 불가능한 값이므로 직접 변경할 수 없기 때문에 재할당을 통해 할당된 원시 값을 새로운 원시 값으로 교체했고, 객체 타입 인수를 전달받은 매개변수 obj의 경우, 객체는 변경 가능한 값이므로 직접 변경할 수 있기 때문에 재할당 없이 직접 할당된 객체를 변경했다.
이때 원시 타입 인수는 값 자체가 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 그 값을 변경(재할당을 통한 교체)해도 원본은 훼손되지 않는다. 다시 말해, 외부상태, 즉 함수외부에서 함수 몸체 내부로 전달한 참조값에 의해 원본 객체가 변경되는 부수 효과가 발생한다.
이처럼 함수가 외부 상태( 객체를 할당한 person 변수 )를 변경하면 상태 변화를 추적하기 어려워진다. 이는 코드와 복잡성을 증가시키고 가독성을 해치는 원인이 된다. 함수 내부의 동작을 유심히 관찰하지 않으면 외부 상태가 변하는지 아닌지 알기 어렵기 때문이다. 언제나 그러하듯 논리가 간단해야 버그가 숨어들지 못한다.
이러한 현상은 객체가 변경할 수 있는 값이며, 참조에 의한 전달 방식으로 동작하기 때문에 발생하는 부작용이다. 여러 변수가 참조에 의한 전달 방식을 통해 참조 값을 공유하고 있다면 이 변수들은 언제든지 참조하고 있는 객체를 직접 변경할 수 있다. 복잡한 코드에서 의도치 않은 객체의 변경을 추적하는 것은 어려운 일이다. 객체의 변경을 추적하려면 옵저버 패턴 등을 통해 객체를 참조를 공유하는 모든 이들에게 변경 사실을 통지하고 이에 대처하는 추가 대응이 필요하다.
이러한 문제의 해결 방법 중 하나는 객체를 불변 객체로 만들어 사용하는 것이다. 객체의 복사본을 새롭게 생성하는 비용은 들지만 객체를 마치 원시값처럼 변경 불가능한 값으로 동작하게 만드는 것이다.
이를 통해 객체의 상태 변경을 원천봉쇄하고 객체의 상태 변경이 필요한 경우에는 객체의 방이적 복사를 통해 원본 객체를 완전히 복제, 즉 깊은 복사를 통해 새로운 객체를 생성하고 재할당을 통해 교체한다. 이를 통해 외부 상태가 변경되는 부수 효과를 없앨 수 있다.
외부 상태를 변경하지 않고 외부 상태에서 의존하지도 않는 함수를 순수 함수라 한다. 순수 함수를 통해 부수 효과를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이려는 프로그래밍 패러다임을 함수형 프로그래밍이라 한다.