JavaScript에는 "참조에 의한 전달 대 참조에 의한 전달"이 없습니다.

Mar 19 2023
"JS 객체는 참조로 전달되고 프리미티브는 값으로 전달된다"는 속담이 전달, 객체 또는 참조와 아무 관련이 없으며 사실 환상인 이유를 보여드리겠습니다. "Objects-are-passed-by-reference"(OAPBR)는 이론/정신적 모델입니다.
화살

"JS 객체는 참조로 전달되고 프리미티브는 값으로 전달된다"는 속담 이 전달, 객체 또는 참조와 아무 관련이 없으며 사실 환상인 이유를 보여드리겠습니다 . "Objects-are-passed-by-reference"(OAPBR)는 이론/정신적 모델입니다. 대부분의 이론과 마찬가지로 이해, 예측, 제어에 대한 기여도와 복잡성 측면에서 비용을 기준으로 판단해야 합니다. 저는 이 예를 매우 부정확한 정신 모델이 다음과 같은 증거로 사용하기를 바랍니다.

  • 고급 기능 학습을 더 어렵게 만듭니다.
  • 다른 프로그래밍 언어 학습을 더 어렵게 만듭니다.
  • 그리고 시간을 낭비합니다. 마지막에는 매우 부정확한 멘탈 모델을 피하기 위한 제안을 할 것입니다.

OAPBR(Objects are Passed by Reference) 이론이 설명하고자 하는 것을 보여주기 위해 먼저 두 가지 정의가 필요합니다.

function plusNum(n1, n2) {
 return n1 + n2;
}

function mutatingAdd(obj1, n) {
 obj1.value = plusNum(obj1.value, n)
}

function takeNum(n /* number */) {
 n = plusNum(n, 1);
 console.log(n); // 2
}

function primitiveExample() {
 let x = 1;
 takeNum(x);
 console.log(x); // 1
}

function mutateObj(obj /* {field: number} */) {
 mutatingAdd(obj, 1);
 console.log(obj.value); // 2
}

function objectExample() {
 let x = {value: 1};
 mutateObj(x);
 console.log(x.value); // 2
}

"객체가 참조로 전달됨"(OAPBR) 이론이 실패하는 방법

이제 함수를 사용하지 않도록 예제를 조정하여 이전 예제가 함수에 값을 전달하는 것과 아무 관련이 없음을 보여 드리겠습니다.

function primitiveExample2() {
 let x = 1;
 let y = x;
 y = y + 1;
 console.log(y); // 2
 console.log(x); // 1
}

function objectExample2() {
 let x = { value: 1 };
 let y = x;
 y.value = y.value + 1;
 console.log(y.value); // 2
 console.log(x.value); // 2
}

function combinedExample1() {
 let x = Object.assign(1, { value: 100 });
 let y = x;
 mutatingAdd(y, 100);
 console.log(y.value); // 200
 console.log(x.value); // 200
 y = plusNum(y, 1);
 console.log(y + 0); // 2
 console.log(x + 0); // 1
}

function nonMutatingPlusObj(obj1, obj2) {
 return { value: obj1.value + obj2.value };
}

function nonMutatingObjectExample() {
 let x = {value: 100};
 let y = x;
 y = nonMutatingPlusObj(y, {value: 100})
 console.log(y.value); // 200
 console.log(x.value); // 100
}

<?php

function add_mut(&$i1, $i2) {
 $i1 += $i2;
}

$x = 1;
add_mut($x, 1);
echo $x; // 2
?>

fn add_mut(i: &mut i32, i2: i32) {
 *i = (*i) + i2;
}

fn main() {
 let i = &mut 1;
 add_mut(i, 1);
 println!("{}", i); // 2
}

function makeRef(thing) {
 return {value: thing};
}

function addMut(iRef, i2) {
 iRef.value = iRef.value + i2;
}

let iRef = makeRef(1);
addMut(iRef, 1);
console.log(iRef.value); // 2

더 나은 이론

"객체는 참조로 전달되고 프리미티브는 값으로 전달됩니다" 대신 범위 변경과 값 변경 사이의 더 나은 구분을 제안합니다. JS에서 범위는 변수 이름에서 값으로의 변경 가능한 매핑입니다.


let x = 1; // the scope maps x to 1
x = 2; // the scope maps x to 2
x += 1; // the scope maps x to 3

각 함수가 새로운 범위를 도입하기 때문에 함수 컨텍스트에서 "참조에 의한 전달" 환상이 발생할 수 있습니다. MDN의 범위에 대해 자세히 알아보십시오 .

아래 코드에서 동일한 개체를 가리키면 x변경 됩니다. 범위를 변경하지 않습니다. JavaScript 함수는 닫히지 않는 범위를 변경할 수 없기 때문입니다.ymutateObjmutatingAdd

function objectExample() {
  let x = {value: 1}; 
  let y = x;
  mutatingAdd(y, 1);
  console.log(y.value); // 2
  console.log(x.value); // 2
}

특히 JS의 경우 범위가 변경 가능하다는 것을 이해하는 것은 const-vs-let, 그림자, 개체 ID, 클로저 및 UI 작성을 이해하는 데 필수적입니다. 그리고 이는 잘못된 "값 기준 대 참조 기준" 구분과 비교할 때 매우 단순한 개념입니다.

무엇 향후 계획

프로그래밍 언어에 대한 나쁜 정신 모델이 다음 소스에서 나오는 것을 보았습니다.

  • 학습 리소스를 사용하지 않고 대신 다이빙하여 프로그래밍을 시도합니다. 이것은 빠르게 느껴질 수 있지만 장기적으로는 느린 경로라고 생각합니다.
  • 블로그 게시물이나 동영상을 통해 학습합니다. (나는 이것이 블로그 게시물이라는 것을 알고 있습니다: 여기에서 무엇보다 MDN을 신뢰하십시오.)
  • 프로그래밍 언어 기능이 명백하다고 가정합니다. 명백한 것은 없으며, 특히 변수는 없으며 프로그래밍 언어 간에 작동 방식에 대한 놀라운 차이가 있습니다.

행복한 해킹!