JavaScript Function - 2

김민기·2022년 9월 3일
0

JavaScript-Study

목록 보기
5/12

이 내용은 이웅모님의 모던 자바스크립트 Deep Dive 책을 공부하고 정리한 내용입니다. (기본 개념부터 심화까지 아주 좋은 책입니다!)
모던 자바스크립트 Deep Dive

Pass by value vs Pass by reference

매개변수로 전달 받는 값이 원시 값인가 객체인가

원시 값은 값에 의한 전달 (pass by value, 쉽게 말해 값을 전달하기 때문에 전달한 값과 전달 받은 값은 서로 다르다.) 객체는 참조에 의한 전달(pass by reference, 쉽게 말해 객체의 주소를 전달하기 때문에 전달한 객체와 전달 받은 객체는 서로 같다.)방식으로 동작한다.

매개변수도 함수 몸체 내부에서 변수와 동일하게 취급되므로 매개변수 또한 타입에 따라 값에 의한 전달, 참조에 의한 전달 방식을 그대로 따른다.

함수를 호출하면서 매개변수에 값을 전달하는 방식을 값에 의한 호출, 참조에 의한 호출로 구별해 부르는 경우도 있으나, 동작 방식은 값에 의한 전달, 참조에 의한 전달과 동일하다.

💡 pass by value === call by value, pass by reference === call by reference
function changeVal(primitive, obj) {
	primitive += 100; // 원시값은 변경 불가능하기 때문에 새로 할당된다.
	obj.name = 'kim'; // 객체는 변경이 가능하기 때문에 원본 객체를 훼손한다.
}

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' } 객체의 원본이 훼손됨.

changeVal 함수는 매개변수를 통해 전달받은 원시 타입 인수와 객체 타입 인수를 함수 몸체에서 변경한다.

→ primitive의 경우 원시 값은 변경 불가능한 값(immutable value)이므로 직접 변경할 수 없기 때문에

재할당을 통해 할당된 원시 값을 새로운 원시 값으로 교체했고

→ obj의 경우 변경 가능한 값(mutable value)이므로 직접 변경할 수 있기 때문에 재할당 없이 직접 할당된 객체를 변경한다.

💡 원시 타입을 인수로 받는 경우 매개변수에 원시 타입으로 재할당이 일어난다.

원시 타입 인수 → 값 자체가 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 그 값을 변경(재할당을 통한 교체)해도 원본은 훼손되지 않는다.

즉 함수 외부에서 함수 몸체 내부로 전달한 원시 값의 원본을 변경하는 어떠한 부수효과도 발생하지 않는다.

객체 타입 인수 → 참조 값이 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 참조 값을 통해 객체를 변경할 경우 원본이 훼손된다.

즉 함수 외부에서 함수 몸체 내부로 전달한 참조 값에 의해 원본 객체가 변경되는 부수효과가 발생한다.

나는 원본 객체를 바꿀 생각은 없었는데

가끔식 알고리즘 문제를 풀거나 코드를 작성할 때 한번씩 실수하는 경우가 있었다.

예를 들어 어떠한 배열을 가지고 for 반복문을 실행하거나 함수에 전달해서 배열의 값을 수정하는 경우 나의 의도는 원본 배열은 유지한채 배열을 변경하고 원본은 유지하고 싶었는데, 반복문을 하다보면 에러가 발생하거나 반복문의 결과가 나의 의도와는 전혀 다른 결과가 나올때가 있었다.

그럴 때마다 코드를 다시 한번 살펴보면, 객체이기 때문에 원본이 훼손되어 발생한 문제라는 것을 금방 깨닫기는 했지만 이렇게 가끔 실수할 때가 있었다.

💡 너무 불편한데?

위 예시 처럼 함수가 외부 상태를 변경하면 (함수 내부에서 obj 객체의 프로퍼티를 수정하는 것, obj는 person과 같은 주소를 가리킨다.) 상태 변화를 추적하기 어려워진다. 이는 코드의 복잡성을 증가시키고 가독성을 해치는 원인이 된다.

이런 현상은 객체가 변경할 수 있는 값이며, 참조에 의한 전달 방식으로 동작하기 때문에 발생하는 부작용이다. 만약 여러 변수가 참조에 의한 전달 방식으로 참조 값을 공유하고 있다면(예를 들어 person을 수정하는 함수가 여러 개 일 경우) 이 변수들은 언제든지 참조하고 있는 객체를 직접 변경할 수 있다. 어디서 변경했는지 추적하기 굉장히 어려워진다.

이러한 문제의 해결 방법 중 하나는 객체를 불변 객체(immutable object)로 만들어 사용하는 것이다. 객체의 복사본을 새롭게 생성하는 비용은 들지만 객체를 마치 원시 값 처럼 변경 불가능한 값으로 동작하게 만드는 것이다.

이를 통해 객체의 상태 변경을 원천봉쇄하고 객체의 상태 변경이 필요한 경우에는 객체의 방어적 복사를 통해 원본 객체를 완전히 복제, 즉 깊은 복사를 통해 새로운 객체를 생성하고 재할당을 통해 교체한다. 이를 통해 외부 상태가 변경되는 부수효과를 없앨 수 있다.

const와 Object.freeze()를 사용해서 불변 객체 만들어보기

const obj = {
	name: 'kim',
}

Object.freeze(obj)

기본적으로 const로 선언된 obj는 객체 재할당은 불가능하지만 객체의 속성은 변경 가능하다. (obj.name:”hi”) Object.freeze는 객체의 속성을 변경할 수 없다. 따라서 이 두가지를 조합해서 객체의 재할당이 불가능하면서 속성도 변경 불가능한 불변 객체를 만들 수 있다.

const obj = {
  name: "kim",
}

Object.freeze(obj);

function changeVal(obj) {
  console.log(obj.name) // kim
  obj.name="hi"
  console.log(obj.name) // kim
}

changeVal(obj);

console.log(obj.name); // kim

obj를 불변 객체로 만들고 changeVal 함수에 인수로 전달한다. 함수 몸체에서 객체의 프로퍼티 name을 변경하지만 변경되지 않는다. 또한 원본 객체인 obj도 전혀 변경되지 않았다.

0개의 댓글