値による関数の呼び出しと参照による関数の呼び出しの違い


1. issue


baekjoon 1590-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++;
        }
      }
    }
  }
}
countをcheckPrev関数のパラメータとしてcheckPrevに渡します.checkPrevを実行するたびにcountを初期化するのではなく、以前の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


これは,JavaScriptでは基本型と参照型の関数呼び出し方式が異なるためである.デフォルトのタイプでは、値呼び出し(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
これに対して、オブジェクトのような参照タイプでは、関数を呼び出すときに参照呼び出し(callby-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と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.以上の原理で作られた1590-フルコードは、バックグラウンドで採点すると「タイムアウト」が発生します.
この問題の解答コードと理由は[規格]15901、2、3プラス5—JavaScript(NodeJS)のブログ記事を参考にした.
注意:
<内部JavaScript>,3.3.2呼び出し関数を参照-p.48
https://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language