값에 의한 함수 호출과 참조에 의한 함수 호출 차이

Maliethy·2022년 4월 2일
0

javascript

목록 보기
4/5

1. issue

baekjoon 15990 - 1, 2, 3 더하기 5 문제를 푸는 도중 다음과 같은 이슈가 발생했다.

//1, 2, 3 더하기 5

makeAddFactors(7)

function makeAddFactors(num) {
  const count = 0;
  // console.log('------------input num', num);
  for (let i = 1; i < 4; i++) {
    if (num === i) {
      count += 1;
    } else {
      checkPrev(num, [i], count);
      // console.log('input result', count);
    }
  }

  return count;
}

function checkPrev(num, temp, count) {
  const tempReduce = temp.reduce((acc, cur) => acc + cur, 0);
  // console.log('count', count);
  // console.log('num-tempReduce', num - tempReduce);
  for (let i = 1; i < 4; i++) {
    if (temp[temp.length - 1] !== i) {
      // console.log('i', i);

      if (tempReduce !== num) {
        if (num - tempReduce > i) {
          // console.log('again------------------');

          checkPrev(num, temp.concat(i), checkCount);
        } else if (num - tempReduce === i) {
          // console.log('final', temp.concat(i));
          count++;
        }
      }
    }
  }
}

checkPrev 함수의 인자로 넘긴 count가 checkPrev가 매번 실행될 때마다 초기화되는 것이 아니라 이전 count값을 계속 기억하고 있다가 값이 더해지길 바랬는데 결과는 다음과 같다.

count 0
count 0
count 0
count 0
count 0
count 0
count 0
count 0
input result 0
count 0
count 0
count 0
count 0
count 0
count 0
count 0
input result 0
count 0
count 0
count 0
count 0
count 0
input result 0

2. solution

이는 자바스크립트에서 기본 타입과 참조 타입의 경우 함수 호출 방식이 다르기 때문이다. 기본 타입의 경우 값에 의한 호출(call by value)방식으로 동작한다. 즉, 함수를 호출할 때 인자로 기본 타입의 값을 넘길 경우, 호출된 함수의 매개변수로 복사된 값이 전달된다. 때문에 함수 내부에서 매개변수를 이용해 값을 변경해도, 실제로 호출된 변수의 값이 변경되지는 않는다.

let a = 'a';

function changeA(a){
        a = 'a-1';
        
        console.log('changeA a:', a);//changeA a: a-1
 }
 
 changeA(a);
 console.log('global a', a);// global a: a

이에 반해 객체와 같은 참조 타입의 경우 함수를 호출할 때 참조에 의한 호출(call by reference)방식으로 동작한다. 즉, 함수를 호출할 때 인자로 참조 타입인 객체를 넘기는 경우, 객체의 프로퍼티값이 함수의 매개변수로 복사되지 않고, 인자로 넘긴 객체의 참조값이 그대로 함수 내부로 전달된다. 때문에 함수 내부에서 참조값을 이용해서 인자로 넘긴 실제 객체의 값을 변경할 수 있다.

const objA = { a: 'a' };
let objB = { b: 'b' };

function changeArg(objA, objB) {
 objA.a = 'a-1';
 objB = { b: 'b-1' };

 console.log('changeArg objA:', objA.a); //changeArg objA: a-1
 console.log('changeArg objB:', objB.b); //changeArg objB: b-1
}

changeA(objA, objB);
console.log('global objA', objA.a); // global objA: a-1
console.log('global objB', objB.b); // global objB: b

objB의 경우 changeArg 함수 내부에서 새로운 객체 { b: 'b-1' }를 할당 하고 있는데 이럴 경우는 global에 있는 objB가 가리키는 객체의 참조값과 다른 참조값을 가리키게 되므로 global과 changeArg 함수 내의 objB의 링크는 끊어진다.

위의 원리를 적용해 해당 코드를 수정하니 다음과 같이 원하는 결과를 얻을 수 있었다.
내가 원하는 값을 얻으려면 checkPrev에 넘기는 count 인자를 객체형태를 참조하는 변수로 만들어 넣으면 된다.

//1, 2, 3 더하기 5

makeAddFactors(7)

function makeAddFactors(num) {
  const checkCount = { count: 0 };//객체 값을 참조하는 checkCount 변수 선언
  // console.log('------------input num', num);
  for (let i = 1; i < 4; i++) {
    if (num === i) {
      checkCount.count += 1;
    } else {
      checkPrev(num, [i], checkCount);
      // console.log('input result', checkCount.count);
    }
  }
  return checkCount.count;
}

function checkPrev(num, temp, checkCount) {
  const tempReduce = temp.reduce((acc, cur) => acc + cur, 0);
  // console.log('count', checkCount.count);
  // console.log('num-tempReduce', num - tempReduce);
  for (let i = 1; i < 4; i++) {
    if (temp[temp.length - 1] !== i) {
      // console.log('i', i);

      if (tempReduce !== num) {
        if (num - tempReduce > i) {
          // console.log('again------------------');

          checkPrev(num, temp.concat(i), checkCount);
        } else if (num - tempReduce === i) {
          // console.log('final', temp.concat(i));
          checkCount.count++;
        }
      }
    }
  }
}
count 0
count 0
count 0
count 0
count 2
count 3
count 3
count 4
input result 5
count 5
count 5
count 5
count 5
count 5
count 6
count 6
input result 7
count 7
count 7
count 7
count 9
count 9
input result 9

p.s. 위의 원리를 적용해 만든 15990 - 전체 코드로 백준사이트에서 채점을 하면 '시간초과'가 난다.
해당 문제의 해답 코드와 이유는 [백준] 15990번 1, 2, 3 더하기 5 - JavaScript(NodeJS) 블로그글을 참고했다.

참고:
<인사이드 자바스크립트>, 3.3.2 참조에 의한 함수 호출 방식 - p.48
https://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language

profile
바꿀 수 있는 것에 주목하자

0개의 댓글