JavaScript는 참조 기준 또는 값 기준 언어입니까?


1404

기본 유형 (숫자, 문자열 등)은 값으로 전달되지만 값을 통해 전달 될 수 있기 때문에 객체를 알 수 없습니다 (객체를 보유한 변수가 실제로 객체를 참조하는 것으로 간주하는 경우) ) 및 참조로 전달 (객체에 대한 변수가 객체 자체를 보유하고 있다고 생각할 때).

마지막에는 중요하지 않지만, 규칙을 통과하는 논쟁을 제시하는 올바른 방법이 무엇인지 알고 싶습니다. JavaScript 사양에 대한 발췌문이 있습니까? 이것에 대한 의미론을 정의해야합니다.


2
우연히 당신이 실수로 값으로 전달 및 참조로 전달에 대한 정의를 뒤집 었다고 생각합니다 ... "값으로 전달 (객체를 보유하는 변수가 실제로 객체에 대한 참조라고 생각하는 경우) 참조로 (객체에 대한 변수가 객체 자체를 보유한다고 생각할 때) "
Niko Bellic

5
예. 구문에 상관없이, 프로그래밍 언어의 함수 호출에서 전달 기준은 전달 된 변수와 연관된 데이터가 함수에 전달 될 때 복사되지 않으므로 전달 된 변수에 대한 함수의 수정 사항이 유지됨을 의미합니다. 함수 호출이 종료 된 후 프로그램에서. 값별 전달은 변수와 연관된 데이터가 실제로 함수에 전달 될 때 복사되고 해당 함수가 해당 변수에 대해 수행 한 수정 사항은 함수가 리턴 할 때 함수가 본문의 범위를 벗어나면 손실됨을 의미합니다.
John Sonderson

5
이 오래된 질문은 크게 반박 된 답변이 틀 렸기 때문에 다소 유독합니다. 자바 스크립트는 엄격히 가치가 있습니다.
Pointy

6
@DanailNachev이 용어는 유감스럽게도 혼란 스럽습니다. "값으로 전달"및 "참조로 전달"은보다 현대적인 프로그래밍 언어 기능보다 먼저 사용되는 용어입니다. "value"및 "reference"라는 단어 는 함수 호출 표현식에 나타나는 매개 변수를 구체적 으로 나타냅니다. JavaScript는 항상 함수를 호출 하기 전에 함수 호출 매개 변수 목록의 각 표현식을 평가 하므로 매개 변수는 항상 값입니다. 혼란스러운 부분은 객체에 대한 참조가 일반적인 JavaScript 값이라는 것입니다. 그러나 이것이 "통과 기준"언어가 아닙니다.
Pointy

2
@DanailNachev "참조로 전달"은 특히 var x=3, y=x; f(x); alert(y === x);함수 f()가 있는 경우 함수 가 경고 보고서를 작성할 수 false있고 그렇지 않다는 것을 의미합니다 true. JavaScript에서는 불가능하므로 참조로 전달되지 않습니다. 수정 가능한 객체에 대한 참조를 전달하는 것이 가능하지만 "참조로 전달"이 의미하는 것은 아닙니다. 내가 말했듯이, 용어가 너무 혼란 스럽다는 것은 부끄러운 일입니다.
Pointy December

답변:


1598

JavaScript에서 흥미 롭습니다. 이 예제를 고려하십시오.

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);
console.log(obj2.item);

출력이 생성됩니다.

10
changed
unchanged
  • 경우 obj1다음 변경, 전혀 참조하지 않았다 obj1.item상의 영향이 없을 것입니다 obj1함수의 외부.
  • 인수가 적절한 참조라면 모든 것이 변경되었을 것입니다. num100, 그리고 obj2.item읽을 것이다 "changed".

대신, 전달 된 품목이 값으로 전달되는 상황입니다. 그러나 가치에 의해 전달되는 항목 자체 는 참조입니다. 기술적으로이를 공유 호출 이라고 합니다 .

당신은 매개 변수 자체 (와 같이 변경하면 실제적인 측면에서,이 방법은 numobj2), 그 매개 변수에 공급 된 항목에 영향을 미치지 않습니다. 그러나 매개 변수 의 내부 를 변경하면 (와 같이 obj1) 전파됩니다 .


32
이것은 C #과 정확히 동일합니다 (적어도 의미 적으로는). Object에는 Value (primitive types)와 Reference의 두 가지 유형이 있습니다.
피터 리

53
나는 이것이 자바에서도 사용된다고 생각한다 : 가치 기준.
Jpnh

295
실제 이유는 changeStuff, num, obj1 및 obj2 내에서 참조이기 때문입니다. itemobj1이 참조하는 객체 의 속성을 변경하면 원래 "변경되지 않음"으로 설정된 항목 속성의 값이 변경됩니다. obj2에 {item : "changed"} 값을 할당하면 참조가 새 객체로 변경됩니다 (함수가 종료되면 즉시 범위를 벗어남). 함수의 이름을 numf, obj1f 및 obj2f와 같이 params로 지정하면 무슨 일이 일어나고 있는지 더 분명해집니다. 그런 다음 매개 변수가 외부 var 이름을 숨기고 있음을 알 수 있습니다.
jinglesthula

13
@BartoNaz 실제로는 아닙니다. 원하는 것은 값으로 참조를 전달하는 대신 참조로 참조를 전달하는 것입니다. 그러나 JavaScript는 다른 값을 모두 전달하는 것처럼 항상 참조를 값으로 전달합니다. 비교를 위해 C #에는 JavaScript 및 Java와 유사한 값별 전달 동작이 있지만 ref키워드를 사용하여 참조 별 전달을 지정할 수 있습니다 . 일반적으로 함수는 새 오브젝트를 리턴하고 수행합니다. 함수를 호출하는 지점에서의 할당 예 : foo = GetNewFoo();대신GetNewFoo(foo);
Tim Goodman

55
이 답변이 가장 인기가 있지만 "순수하게 통과 한 경우"라고 표시되어 있기 때문에 약간 혼동 될 수 있습니다. 자바 스크립트 순수한 가치입니다. 그러나 전달 된 값은 참조입니다. 이것은 매개 변수 전달로 제한되지 않습니다. 단순히 변수를 복사 var obj1 = { item: 'unchanged' }; var obj2 = obj1; obj2.item = 'changed';하여 예제와 동일한 효과를 관찰 할 수 있습니다. 그러므로 나는 개인적으로 Tim Goodman의 답변을 인용합니다
chiccodoro

476

항상 값으로 전달되지만 객체의 경우 변수 값이 참조입니다. 이로 인해 객체를 전달하고 멤버 를 변경하면 해당 변경 사항이 함수 외부에 유지됩니다. 이것은 참조로 전달하는 것처럼 보입니다 . 그러나 실제로 객체 변수의 값을 변경하면 변경 사항이 지속되지 않고 실제로 값을 통과한다는 것을 알 수 있습니다.

예:

function changeObject(x) {
  x = { member: "bar" };
  console.log("in changeObject: " + x.member);
}

function changeMember(x) {
  x.member = "bar";
  console.log("in changeMember: " + x.member);
}

var x = { member: "foo" };

console.log("before changeObject: " + x.member);
changeObject(x);
console.log("after changeObject: " + x.member); /* change did not persist */

console.log("before changeMember: " + x.member);
changeMember(x);
console.log("after changeMember: " + x.member); /* change persists */

산출:

before changeObject: foo
in changeObject: bar
after changeObject: foo

before changeMember: foo
in changeMember: bar
after changeMember: bar

14
@daylight : 사실, 당신은 틀 렸습니다; const ref가 전달하면 changeObject를 시도하면 오류가 아닌 오류가 발생합니다. C ++에서 const 참조에 새 값을 할당하면 컴파일러에서이를 거부합니다. 사용자 용어로, 그것은 값으로 전달과 const 기준으로 전달의 차이입니다.
deworde

5
@ daylight : 상수가 아닙니다. 에서은 changeObject, 내가 변경 한 x새 개체에 대한 참조를 포함 할 수 있습니다. x = {member:"bar"};에 해당 x = new Object(); x.member = "bar"; 하는 방식으로, 또한 C 번호의 사실 내가 말하는거야.
Tim Goodman

2
@daylight : C #의 경우 함수 외부에서 이것을 볼 수 있습니다. ref키워드 를 사용 하면 참조를 값으로 전달하는 기본값 대신 참조로 참조를 전달하면 a를 가리키는 변경 사항 new Object() 지속됩니다. .
Tim Goodman

11
@adityamenon "왜"라고 대답하기는 어렵지만 Java와 C #의 디자이너는 비슷한 선택을했다는 점에 주목할 것입니다. 이것은 JavaScript의 이상한 점이 아닙니다. 실제로, 그것은 매우 일관되게 가치를 전달합니다. 사람들에게 혼란을주는 것은 가치가 참조가 될 수 있다는 것입니다. C ++에서 (값으로) 포인터를 전달한 다음 참조를 해제하여 멤버를 설정하는 것과 크게 다르지 않습니다. 그 변화가 지속된다는 사실에 아무도 놀라지 않을 것입니다. 그러나 이러한 언어는 포인터를 추상화하고 자동으로 역 참조를 수행하므로 사람들은 혼란에 빠집니다.
Tim Goodman

41
다시 말해서, 여기서 혼동되는 것은 가치 별 / 기준 별이 아닙니다. 모든 것은 가치에 의한 완전 정지입니다. 혼란스러운 것은 객체를 전달할 수 없으며 객체를 변수에 저장할 수 없다는 것입니다. 당신이 때마다 생각 당신이 그 일을하고, 당신이있어 실제로 통과 또는 객체에 대한 참조를 저장. 그러나 멤버에 액세스 할 때 변수가 실제 객체를 가지고 있다는 허구를 영속시키는 침묵하는 역 참조가 발생합니다.
Tim Goodman

150

변수는 객체를 "보유"하지 않습니다. 참조를 보유합니다. 해당 참조를 다른 변수에 할당 할 수 있으며 이제 둘 다 동일한 객체를 참조합니다. 항상 값으로 전달됩니다 (값이 참조 일 때도).

매개 변수로 전달 된 변수가 보유한 값을 변경할 방법이 없습니다. JavaScript가 참조로 전달을 지원하는 경우 가능합니다.


2
이것은 나를 조금 혼란스럽게합니다. 참조에 의한 참조를 전달하지 않습니까?

8
저자는 참조를 전달함으로써 참조 값을 전달한다는 것을 의미합니다 (메모리 주소의 값을 전달하는 다른 방법). 따라서 객체를 다시 선언하면 다른 메모리 위치에 새 객체를 작성하기 때문에 원본이 변경되지 않습니다. 속성을 변경하면 원래 메모리 위치 (재 할당되지 않은)에서 변경했기 때문에 원래 개체가 변경됩니다.
Huy-Anh Hoang

113

내 두 센트 ... 이것이 내가 이해하는 방식입니다. (내가 틀렸다면 자유롭게 고쳐주십시오)

이제 당신이 아는 모든 것을 가치 / 기준으로 전달할 때입니다.

JavaScript에서는 값으로 전달되는지 또는 참조로 전달되는지 여부는 중요하지 않습니다. 중요한 것은 함수에 전달 된 매개 변수의 돌연변이 대 돌연변이입니다.

자, 제가 의미하는 바를 설명하기 위해 최선을 다하겠습니다. 몇 가지 물건이 있다고 가정 해 봅시다.

var object1 = {};
var object2 = {};

우리가 한 것은 "할당"... 변수 "object1"과 "object2"에 2 개의 별도의 빈 개체를 할당했습니다.

이제 object1을 더 좋아한다고 가정 해 봅시다. 그래서 새로운 변수를 "할당"합니다.

var favoriteObject = object1;

다음으로, 어떤 이유로 든 객체 2를 더 좋아하기로 결정합니다. 그래서 우리는 단순히 약간의 재 할당을합니다.

favoriteObject = object2;

object1 또는 object2에 아무런 변화가 없습니다. 우리는 데이터를 전혀 변경하지 않았습니다. 우리가 한 일은 우리가 가장 좋아하는 물건을 다시 할당하는 것입니다. object2와 favoriteObject가 모두 동일한 오브젝트에 지정되어 있음을 알아야합니다. 변수 중 하나를 통해 해당 개체를 변경할 수 있습니다.

object2.name = 'Fred';
console.log(favoriteObject.name) // Logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // Logs Joe

자, 예를 들어 문자열과 같은 프리미티브를 살펴 보겠습니다.

var string1 = 'Hello world';
var string2 = 'Goodbye world';

다시, 우리는 마음에 드는 것을 골라냅니다.

var favoriteString = string1;

우리의 favoriteString과 string1 변수는 모두 'Hello world'에 할당됩니다. 이제 favoriteString을 변경하려면 어떻게해야합니까 ??? 무슨 일이 일어날 것???

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'

어 .. 무슨 일이야. favoriteString을 변경하여 string1을 변경할 수 없습니다 ... 왜 ?? 문자열 객체를 변경 하지 않았기 때문 입니다. 우리가 한 것은 favoriteString 변수 를 새 문자열 에 "RE ASSIGN"하는 것뿐입니다 . 이것은 본질적으로 string1에서 연결을 끊었습니다. 이전 예제에서 객체의 이름을 바꿀 때 아무 것도 할당하지 않았습니다. ( 변수 자체가 아니라 , name 속성을 새 문자열에 할당했습니다.) 대신, 단순히 2 개의 변수와 기본 개체 사이의 연결을 유지하는 개체를 변경했습니다. ( 문자열 객체 자체 를 수정하거나 변경 하려는 경우에도문자열은 실제로 JavaScript에서 변경할 수 없기 때문에 가질 수 없었습니다.)

이제 함수와 매개 변수 전달에 대해 ... 함수를 호출하고 매개 변수를 전달할 때 기본적으로 수행하는 작업은 새 변수에 "할당"하는 것입니다. 등호 (=) 부호

이 예제들을 보자.

var myString = 'hello';

// Assign to a new variable (just like when you pass to a function)
var param1 = myString;
param1 = 'world'; // Re assignment

console.log(myString); // Logs 'hello'
console.log(param1);   // Logs 'world'

자, 똑같은 기능이지만

function myFunc(param1) {
    param1 = 'world';

    console.log(param1);   // Logs 'world'
}

var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString);

console.log(myString); // logs 'hello'

자, 이제 함수없이 먼저 객체를 사용하는 몇 가지 예를 들어 보겠습니다.

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;

// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl

console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'

// Now, let's reassign the variable
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object has no influence on the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

자, 똑같은 것이지만 함수 호출

function myFunc(otherObj) {

    // Let's mutate our object
    otherObj.firstName = 'Sue';
    console.log(otherObj.firstName); // Logs 'Sue'

    // Now let's re-assign
    otherObj = {
        firstName: 'Jack',
        lastName: 'Frost'
    };
    console.log(otherObj.firstName); // Logs 'Jack'

    // Again, otherObj and myObject are assigned to 2 very different objects
    // And mutating one object doesn't magically mutate the other
}

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);

console.log(myObject.firstName); // Logs 'Sue', just like before

이 글 전체를 읽으면 JavaScript에서 함수 호출이 어떻게 작동하는지 더 잘 이해할 수있을 것입니다. 무언가가 참조에 의해 전달되는지 또는 가치에 의해 전달되는지는 중요하지 않습니다 ... 중요한 것은 할당 대 돌연변이입니다.

변수를 함수에 전달할 때마다 등호 (=) 기호를 사용하는 것처럼 매개 변수 변수의 이름이 무엇이든 "할당"됩니다.

항상 등호 (=)는 할당을 의미합니다. JavaScript에서 매개 변수를 함수 전달하는 것도 할당을 의미한다는 것을 항상 기억하십시오 . 그것들은 동일하고 2 개의 변수는 정확히 같은 방식으로 연결됩니다 (즉, 동일한 객체에 할당되었다고 계산하지 않는 한 그렇지 않습니다).

"변수 수정"이 다른 변수에 영향을 미치는 유일한 시간은 기본 개체가 변경되는 경우입니다 (이 경우 변수를 수정하지 않고 개체 자체).

객체와 프리미티브를 구별 할 필요는 없습니다. 왜냐하면 함수가없는 것과 같은 방식으로 작동하고 등호를 사용하여 새 변수에 할당했기 때문입니다.

함수에 전달하는 변수의 이름이 함수 매개 변수의 이름과 같은 경우가 유일한 문제입니다. 이런 일이 발생하면 함수 내부의 매개 변수를 함수 전용의 완전히 새로운 변수 인 것처럼 취급해야합니다.

function myFunc(myString) {
    // myString is private and does not affect the outer variable
    myString = 'hello';
}

var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';

myFunc(myString);
console.log(myString); // Logs 'test'

2
모든 C 프로그래머는 char *를 생각하십시오. foo(char *a){a="hello";} 아무것도하지 않지만 문자열 (char 배열)을 참조하는 값으로 메모리 위치가 전달 foo(char *a){a[0]='h';a[1]='i';a[2]=0;}되므로 외부에서 변경 a됩니다. C에서 값으로 구조체 (js 객체와 유사)를 전달하는 것은 허용되지만 권장되지는 않습니다. JavaScript는 단순히 이러한 모범 사례를 시행하고 불필요하고 일반적으로 원하지 않는 균열을 숨기고 더 쉽게 읽을 수 있도록합니다.
technosaurus

2
이 올바른지 - 용어가 에 의해 가치 패스패스에 의해 참조 언어 설계 프로그래밍의 의미를 가지고 있고, 그 의미는 개체 변이와는 전혀 아무것도 없다. 함수 매개 변수의 작동 방식에 관한 것입니다.
Pointy

2
이제 obj1 = obj2는 obj1과 obj2가 모두 동일한 참조 위치를 가리키고 있음을 이해하고 obj2의 내부를 수정하면 obj1을 참조하면 동일한 내부가 노출됩니다. source = { "id":"1"}; copy = source /*this is wrong*/; copy.id="2"해당 소스를 수행 할 때 여전히 { "id": "1"} 이 되도록 오브젝트를 어떻게 복사 합니까?
Machtyn

1
혼란을 줄이기 위해 전통적인 정의로 또 다른 답변을 게시했습니다. "값별 통과"및 "기준 별 통과"에 대한 기존 정의는 자동 역 참조 전에 메모리 포인터 날짜에 다시 정의되었습니다. 객체 변수의 값은 실제로 객체가 아니라 메모리 포인터 위치라는 것을 완전히 이해했습니다. 과제 대 돌연변이에 대한 논의가 유용 할 수도 있지만, 전통적인 용어 나 그 정의를 버릴 필요는 없습니다. 돌연변이, 할당, 값별, 참조 별 등이 서로 모순되지 않아야합니다.
C Perkins

"Number"도 "불변"입니까?
ebram khalil

72

다음을 고려하세요:

  1. 변수는 메모리의 값을 가리키는 포인터 입니다.
  2. 변수를 다시 할당하면 해당 포인터가 새로운 값을 가리 킵니다.
  3. 변수를 다시 할당해도 동일한 객체를 가리키는 다른 변수에는 영향을 미치지 않습니다

그래서 잊어 "참조 / 값에 의해 패스" 에 끊었되지 않기 때문에 "참조 / 값 통과"

  1. 이 용어는 언어 의 동작 을 설명하는 데만 사용되며 실제 실제 구현은 아닙니다. 이러한 추상화의 결과로, 적절한 설명에 필수적인 중요한 세부 사항이 손실되며, 이는 단일 용어가 실제 행동을 적절하게 설명하지 못하고 추가 정보를 제공해야하는 현재 상황으로 이어질 수밖에 없습니다
  2. 이 개념은 원래 자바 스크립트를 설명하려는 의도로 정의되지 않았으므로 혼란에 빠질 때만 사용하도록 강요하지 않습니다.

귀하의 질문에 대답하기 위해 : 포인터가 전달됩니다.


// code
var obj = {
    name: 'Fred',
    num: 1
};

// illustration
               'Fred'
              /
             /
(obj) ---- {}
             \
              \
               1


// code
obj.name = 'George';


// illustration
                 'Fred'


(obj) ---- {} ----- 'George'
             \
              \
               1


// code
obj = {};

// illustration
                 'Fred'


(obj)      {} ----- 'George'
  |          \
  |           \
 { }            1


// code
var obj = {
    text: 'Hello world!'
};

/* function parameters get their own pointer to 
 * the arguments that are passed in, just like any other variable */
someFunc(obj);


// illustration
(caller scope)        (someFunc scope)
           \             /
            \           /
             \         /
              \       /
               \     /
                 { }
                  |
                  |
                  |
            'Hello world'

마지막 의견 :

  • 프리미티브 는 특수 규칙에 의해 시행되는 반면 객체 는 그렇지 않지만 프리미티브는 단순히 포인터 체인의 끝 이라고 생각하고 싶어합니다 .
  • 마지막 예로 배열을 지우는 일반적인 시도가 예상대로 작동하지 않는 이유를 고려하십시오.


var a = [1,2];
var b = a;

a = [];
console.log(b); // [1,2]
// doesn't work because `b` is still pointing at the original array

추가 크레딧에 대한 후속 질문;) 가비지 수집은 어떻게 작동합니까? 백만 개의 {'George', 1}값을 통해 변수를 순환 하지만 한 번에 하나만 사용하면 다른 변수 는 어떻게 관리됩니까? 변수를 다른 변수의 값에 할당하면 어떻게됩니까? 그런 다음 포인터를 가리 키거나 오른쪽 피연산자의 포인트를 가리 킵니까? 또는을 가리키는 var myExistingVar = {"blah", 42}; var obj = myExistingVar;결과가 발생 합니까 ? obj{"blah", 42}myExistingVar
Michael Hoffmann

@MichaelHoffmann 이들은 자신의 SO 질문을받을 자격이 있으며 아마도 내가 관리 할 수있는 것보다 이미 더 나은 답변을 얻었습니다. 즉, 1)설명 한 것과 같은 루프 기능을 위해 브라우저 개발 도구에서 메모리 프로파일을 실행했으며 루핑 프로세스 전체에서 메모리 사용량이 급증했습니다. 이것은 루프의 각 반복에서 새로운 동일한 객체가 실제로 생성되고 있음을 나타냅니다. 스파이크가 갑자기 떨어지면 가비지 수집기에서 이러한 사용되지 않는 개체 그룹을 정리했습니다.
geg

1
@MichaelHoffmann 2)와 같은 것과 관련하여 var a = bjavascript는 포인터를 사용하는 메커니즘을 제공하지 않으므로 기본 javascript 엔진이 의심 할 여지없이 변수를 포인터를 가리킬 수 없습니다 (C 에서처럼). 따라서 ... "오른쪽 피연산자의 var a = b포인트 a"를 가리 킵니다
geg

여기에 질문 # 1 (특히 브라우저마다 구현이 다르기 때문에 Chrome에 관한 질문) stackoverflow.com/q/42778439/539997 그리고 질문 # 2를 단어로 쓰는 방법을 여전히 생각하고 있습니다. 도움을 주시면 감사하겠습니다.
Michael Hoffmann

1
"참조 / 값으로 전달"잊을 필요가 없습니다 ! 이 용어들은 당신이 묘사하려는 것을 정확하게 묘사하는 역사적 의미를 가지고 있습니다. 역사적 용어와 정의를 버리고 원래 의미를 배우기에는 너무 게으 르면 세대간에 효과적으로 의사 소통 할 수있는 능력을 잃게됩니다. 다른 언어와 시스템의 차이점을 논의 할 수있는 좋은 방법은 없습니다. 대신, 새로운 프로그래머는 전통적인 용어와 그 이유와 이유를 배우고 이해해야합니다. 그렇지 않으면, 우리는 총체적으로 지식과 이해를 잃습니다.
C Perkins

24

함수 외부의 객체는 외부 객체에 대한 참조를 제공하여 함수로 전달됩니다.

해당 참조를 사용하여 객체를 조작하면 외부 객체가 영향을받습니다. 그러나 함수 내부에서 다른 것에 대한 참조를 가리 키기로 결정한 경우 외부에있는 객체에 전혀 영향을 미치지 않았습니다.


20

이것을 다음과 같이 생각하십시오 : 그것은 항상 가치를 지니고 있습니다. 그러나 객체의 값은 객체 자체가 아니라 해당 객체에 대한 참조입니다.

다음은 숫자 (원시 유형)를 전달하는 예입니다.

function changePrimitive(val) {
    // At this point there are two '10's in memory.
    // Changing one won't affect the other
    val = val * 10;
}
var x = 10;
changePrimitive(x);
// x === 10

이것을 객체와 함께 반복하면 다른 결과가 나타납니다.

function changeObject(obj) {
    // At this point there are two references (x and obj) in memory,
    // but these both point to the same object.
    // changing the object will change the underlying object that
    // x and obj both hold a reference to.
    obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
// x === { val: 100 }

하나 더 예 :

function changeObject(obj) {
    // Again there are two references (x and obj) in memory,
    // these both point to the same object.
    // now we create a completely new object and assign it.
    // obj's reference now points to the new object.
    // x's reference doesn't change.
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}

19

값과 참조에 의한 복사, 전달 및 비교에 대한 매우 자세한 설명은 "JavaScript : Definitive Guide"의이 장 에 있습니다.

객체와 배열 조작이라는 주제를 참조로 떠나기 전에 명명법을 정리해야합니다.

문구 "통과 기준"은 여러 가지 의미를 가질 수 있습니다. 일부 독자에게이 문구는 함수가 새로운 값을 인수에 할당하고 수정 된 값을 함수 외부에서 볼 수 있도록하는 함수 호출 기술을 말합니다. 이것은이 책에서 용어가 사용되는 방식이 아닙니다.

여기서 우리는 단순히 객체 자체가 아닌 객체 또는 배열에 대한 참조가 함수에 전달된다는 것을 의미합니다. 함수는 참조를 사용하여 배열의 객체 또는 요소의 속성을 수정할 수 있습니다. 그러나 함수가 참조를 새 객체 또는 배열에 대한 참조로 덮어 쓰면 해당 수정은 함수 외부에서 볼 수 없습니다.

이 용어의 다른 의미에 익숙한 독자는 객체와 배열이 값으로 전달되는 것을 선호하지만 전달되는 값은 실제로는 객체 자체가 아닌 참조입니다.


와우, 이것은 매우 혼란 스럽습니다. 올바른 생각을 가진 사람은 정반대 를 의미하는 잘 정립 된 용어를 정의한 다음 그런 식으로 사용합니까? 이 질문에 대한 많은 답변이 혼란 스럽습니다.
Jörg W Mittag

16

JavaScript는 항상 가치를 전달합니다 . 모든 것이 가치 유형입니다.

객체는 값이고 객체의 멤버 함수는 값 자체입니다 (함수는 JavaScript에서 일류 객체임을 기억하십시오). 또한 JavaScript의 모든 것이 객체 라는 개념과 관련하여 ; 이것은 잘못이다. 문자열, 기호, 숫자, 부울, 널 및 정의되지 않은 것은 기본 요소 입니다.

때로는 기본 프로토 타입에서 상속 된 일부 멤버 함수 및 속성을 활용할 수 있지만 이는 단지 편의를위한 것입니다. 그것들이 객체 자체라는 의미는 아닙니다. 다음을 참조하십시오.

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

두 경고 모두 값이 정의되지 않은 것을 알 수 있습니다.


12
-1, 항상 값으로 전달되는 것은 아닙니다. MDC에서 : "객체 (예 : Array 또는 사용자 정의 객체와 같은 기본이 아닌 값)를 매개 변수로 전달하면 해당 객체에 대한 참조가 함수로 전달됩니다."
Nick

37
@Nick : 항상 값으로 전달됩니다. 기간. 객체에 대한 참조는 으로 함수에 전달 됩니다 . 그것은 참조로 전달되지 않습니다. "Pass by reference"는 변수 값 대신 변수 자체를 전달하는 것으로 생각할 수 있습니다. 어느 인수로 함수 차종을 변경 (완전히 다른 객체로 대체 포함!) 호출자에 반영 될 것이다. JS는 참조로 전달하지 않기 때문에 마지막 비트는 불가능합니다. JS 는 참조를 값으로 전달합니다. 구별은 미묘하지만 그 한계를 이해하는 데 중요합니다.
cHao

1
미래의 스태커들에게 ... 당신의이 참조에 관하여 : x = "teste"; x.foo = 12;등등. 프로퍼티가 영속적이지 않다고해서 그것이 객체가 아니라는 의미는 아닙니다. MDN의 말처럼 : JavaScript에서는 거의 모든 것이 객체입니다. null 및 undefined를 제외한 모든 기본 유형은 객체로 처리됩니다. 속성을 할당 할 수 있으며 (일부 유형의 할당 된 속성은 영구적이지 않음) 객체의 모든 특성을 갖습니다. 링크
slacktracer

9
MDN은 사용자 편집 위키이며 잘못되었습니다. 규범 참조는 ECMA-262입니다. 참조 해결 방법을 설명하는 S. 8 "참조 사양 유형"및 참조에 대한 AssignmentExpression을 설명하는 데 사용되는 8.12.5 "[[Put]]"및 객체 강제 변환 9.9 ToObject를 참조하십시오. 기본 값의 경우 Michael은 사양에서와 같이 ToObject의 기능을 이미 설명했습니다. 그러나 s 참조. 4.3.2 원시 값.
개럿

1
@WonderLand : 아뇨. 참조로 전달할 수 없었던 사람들은 참조로 전달하는 것과 값으로 참조를 전달하는 것의 차이점을 절대 이해할 수 없습니다. 그러나 그들은 거기에 있습니다. 사람들에게 잘못된 정보를주는 것만 신경 쓰지 않아도됩니다.
cHao

12

JavaScript에서 값의 유형은 해당 값 을 value-copy 또는 reference-copy 로 지정할지 여부 제어합니다 .

기본 값은 항상 value-copy에 의해 지정 / 전달됩니다 .

  • null
  • undefined
  • 번호
  • 부울
  • 기호 ES6

복합 값은 항상 참조-복사에 의해 할당 / 전달됩니다

  • 사물
  • 배열
  • 함수

예를 들어

var a = 2;
var b = a; // `b` is always a copy of the value in `a`
b++;
a; // 2
b; // 3

var c = [1,2,3];
var d = c; // `d` is a reference to the shared `[1,2,3]` value
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]

위의 스 니펫 2은 스칼라 프리미티브이므로 a해당 값의 초기 사본 하나를 보유하고 값의 b다른 사본이 지정됩니다. 변경할 때 b의 값을 변경하는 것은 아닙니다 a.

그러나 모두 cd동일한 공유 값 별도 참조로 [1,2,3]복합 값이다. 값을 "소유" c하지도 않고 둘 다 값에 대한 피어 참조와 동일하다는 점에 유의해야 합니다. 따라서 참조를 사용 하여 실제 공유 값 자체 를 수정 ( ) 하면 하나의 공유 값에만 영향을 미치며 두 참조 모두 새로 수정 된 값을 참조합니다 .d[1,2,3].push(4)array[1,2,3,4]

var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]

// later
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]

b = [4,5,6]우리가 과제를 할 때 , 우리 a는 여전히 참조하는 곳 ( [1,2,3]) 에 영향을 미치지 않습니다 . 그렇게하려면에 대한 참조보다는 b포인터를 가리켜 야합니다. 그러나 JS에는 그러한 기능이 없습니다!aarray

function foo(x) {
    x.push( 4 );
    x; // [1,2,3,4]

    // later
    x = [4,5,6];
    x.push( 7 );
    x; // [4,5,6,7]
}

var a = [1,2,3];

foo( a );

a; // [1,2,3,4]  not  [4,5,6,7]

인수를 전달 a하면 a참조 사본이에 할당 됩니다 x. xa같은 가리키는 별도의 참조입니다 [1,2,3]값. 이제 함수 내에서 해당 참조를 사용하여 값 자체를 변경할 수 있습니다 ( push(4)). 그러나 할당 할 때 x = [4,5,6]이것은 초기 참조 a가 가리키는 위치에 영향을 미치지 않으며 여전히 (현재 수정 된) [1,2,3,4]값을 가리 킵니다 .

array값 복사 로 복합 값 ()을 효과적으로 전달하려면 전달 된 참조가 여전히 원본을 가리 키지 않도록 수동으로 사본을 만들어야합니다. 예를 들면 다음과 같습니다.

foo( a.slice() );

참조 복사로 전달할 수있는 복합 값 (객체, 배열 등)

function foo(wrapper) {
    wrapper.a = 42;
}

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

여기 obj에서 스칼라 프리미티브 속성의 래퍼 역할을합니다 a. 에 전달되면 참조 foo(..)사본 obj이 전달되어 wrapper매개 변수로 설정됩니다 . 이제 wrapper참조를 사용하여 공유 객체에 액세스하고 속성을 업데이트 할 수 있습니다. 함수가 완료되면 obj.a업데이트 된 값이 표시됩니다 42.

출처


당신이 처음 상태 "복합 값은 항상 참조 복사에 의해 전달 / 할당"다음 주 "할당하는 사본 x에 대한 참조의". "복합 값"이라고 부르는 경우 실제 변수 값은 참조입니다 (예 : 메모리 포인터). 설명했듯이 참조가 복사됩니다. 따라서 변수 값이 복사 되어 참조가 값임을 강조합니다. 이는 JavaScript가 모든 유형에 대해 가치를 전달한다는 것을 의미합니다. 값별 전달은 변수 값의 사본을 전달하는 것을 의미합니다. 값이 객체 / 배열에 대한 참조인지는 중요하지 않습니다.
C Perkins

새로운 용어 (값 복사 / 참조 복사)를 도입하면 상황이 더욱 복잡해집니다. 단지 사본, 기간이 있습니다. 프리미티브를 전달하면 실제 프리미티브 데이터의 사본을 전달하고 오브젝트를 전달하면 오브젝트의 메모리 위치 사본을 전달합니다. 그게 당신이 말해야 할 전부입니다. 더 많은 것은 사람들을 혼란스럽게합니다.
Scott Marcus

9

글쎄, 그것은 '성능'과 '속도'에 관한 것이고 프로그래밍 언어에서 간단한 단어 '메모리 관리'에 관한 것입니다.

: 자바 스크립트에서는 두 층의 값을 넣을 수있는 타입 1 - objectsTYPE2 같은 값 - 모든 다른 유형 stringboolean및 기타

메모리를 아래 사각형으로 생각하면 각각 하나의 type2 값만 저장할 수 있습니다.

여기에 이미지 설명을 입력하십시오

모든 type2 값 (녹색)은 단일 사각형이고 type1 값 (파란색)은 그 그룹입니다 .

여기에 이미지 설명을 입력하십시오

요점은 type2 값을 나타내려면 주소가 명확하지만 type1 값에 대해 동일한 작업을 수행하려는 경우 전혀 쉽지 않다는 것입니다. :

여기에 이미지 설명을 입력하십시오

더 복잡한 이야기에서 :

여기에 이미지 설명을 입력하십시오

그래서 여기 참조 는 우리를 구출 할 수 있습니다 : 여기에 이미지 설명을 입력하십시오

여기서 녹색 화살표는 일반적인 변수이지만 자주색 변수는 객체 변수이므로 녹색 화살표 (일반 변수)에는 작업이 하나뿐 이므로 (일반적인 값을 나타냄) 값을 구분할 필요가 없습니다. 그것은 우리가가는 곳마다 그리고 모든 할당, 기능 등에서 녹색 화살표를 그 값으로 움직입니다 ...

그러나 우리는 자주색 화살표로 같은 일을 할 수 없습니다. 우리는 'john'셀을 여기 또는 다른 많은 것들로 옮기고 싶을 수도 있습니다. ...

매우 혼란스러운 상황은 참조 된 변수가 어떻게 변경되는지 알 수없는 곳입니다. 아주 좋은 예를 살펴 보겠습니다.

let arr = [1, 2, 3, 4, 5]; //arr is an object now and a purple arrow is indicating it
let obj2 = arr; // now, obj2 is another purple arrow that is indicating the value of arr obj
let obj3 = ['a', 'b', 'c'];
obj2.push(6); // first pic below - making a new hand for the blue circle to point the 6
//obj2 = [1, 2, 3, 4, 5, 6]
//arr = [1, 2, 3, 4, 5, 6]
//we changed the blue circle object value (type1-value) and due to arr and obj2 are indicating that so both of them changed
obj2 = obj3; //next pic below - changing the direction of obj2 array from blue circle to orange circle so obj2 is no more [1,2,3,4,5,6] and it's no more about changing anything in it but we completely changed its direction and now obj2 is pointing to obj3
//obj2 = ['a', 'b', 'c'];
//obj3 = ['a', 'b', 'c'];

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오


8

값으로 전달 및 참조로 전달 (JavaScript)에 대한 자세한 설명입니다. 이 개념에서는 변수를 참조로 전달하고 변수를 참조로 전달하는 것에 대해 이야기합니다.

값으로 전달 (기본 유형)

var a = 3;
var b = a;

console.log(a); // a = 3
console.log(b); // b = 3

a=4;
console.log(a); // a = 4
console.log(b); // b = 3
  • JavaScript의 모든 기본 유형 (문자열, 숫자, 부울, 정의되지 않은 및 널)에 적용됩니다.
  • a는 메모리 (0x001)에 할당되고 b는 메모리 (0x002)에 값의 사본을 만듭니다.
  • 따라서 변수 값을 변경해도 다른 두 위치에 상주하므로 다른 값에는 영향을 미치지 않습니다.

참조로 전달 (객체)

var c = { "name" : "john" };
var d = c;

console.log(c); // { "name" : "john" }
console.log(d); // { "name" : "john" }

c.name = "doe";

console.log(c); // { "name" : "doe" }
console.log(d); // { "name" : "doe" }
  • JavaScript 엔진은 객체를 변수에 할당하고 c(0x012)와 같은 일부 메모리를 가리 킵니다.
  • d = c 인 경우이 단계 d에서 동일한 위치 (0x012)를 가리 킵니다.
  • 변수 값을 변경하면 변수에 대한 값이 모두 변경됩니다.
  • 함수는 객체입니다

특수 사례, 참조로 전달 (객체)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • equal (=) 연산자는 새로운 메모리 공간 또는 주소를 설정합니다

소위 특수한 경우에는 메모리 공간을 할당하는 것이 할당 연산자가 아니라 객체 리터럴 자체입니다. 컬리 괄호 표기법으로 인해 새 객체가 생성됩니다. 이 속성 c은 새 개체 참조의 복사본으로 설정됩니다.
georgeawg 2016 년

6

JavaScript에서 내가 아는 것을 공유

JavaScript에서 객체를 변수에 할당 할 때 변수에 지정된 값은 객체에 대한 참조입니다.

var a = {
  a: 1,
  b: 2,
  c: 3
};
var b = a;

// b.c is referencing to a.c value
console.log(b.c) // Output: 3
// Changing value of b.c
b.c = 4
// Also changes the value of a.c
console.log(a.c) // Output: 4


1
이것은 이전의 답변이 더 잘 설명되지 않았다는 것을 말하지 않는 지나치게 단순한 답변입니다. 특별한 경우로 배열을 호출하는 이유가 혼란 스럽습니다.
Quentin

1
" 객체는 참조로 저장됩니다 "는 잘못된 것입니다. 내가 생각하는 것은 변수에 객체를 할당 할 때 변수에 할당 된 값이 객체에 대한 참조라는 것입니다.
RobG

함수 외부의 오브젝트를 업데이트하지 않는 함수 내부의 오브젝트 업데이트 문제는 다루지 않습니다. 그것이 참조가 아닌 값으로 작동하는 것처럼 보이는 전체 그림입니다. 따라서 -1
amaster

@amaster 지적 해 주셔서 감사합니다! 수정 사항
xameeramir

하하, 나는 시도했다 ... 내가 제안한 편집이 너무 많이 변경되었습니다 amd는 허용되지 않았습니다
amaster

4

의미론!! 구체적인 정의를 설정하면 동일한 단어와 문구를 사용할 때도 동일한 내용을 설명하지 않기 때문에 일부 답변과 의견이 호환되지 않을 수 있지만 혼동을 피하는 것이 중요합니다 (특히 새로운 프로그래머의 경우).

우선, 모든 사람이 파악하지 못하는 추상화 수준은 여러 가지입니다. 4 세대 또는 5 세대 언어를 배운 신입 프로그래머는 포인터에 대한 포인터로 단계적으로 표시되지 않은 어셈블리 또는 C 프로그래머에게 친숙한 개념을 염두에 두지 못할 수 있습니다. 참조 별 통과는 단순히 함수 매개 변수를 사용하여 참조 된 객체를 변경하는 기능을 의미하지는 않습니다.

변수 : 메모리의 특정 위치에서 값을 참조하는 기호의 결합 된 개념. 이 용어는 일반적으로 세부 사항을 논의 할 때 단독으로 사용하기에는 너무로드됩니다.

Symbol : 변수 (예 : 변수 이름)를 나타내는 데 사용되는 텍스트 문자열입니다.

Value : 메모리에 저장되고 변수의 심볼을 사용하여 참조되는 특정 비트.

메모리 위치 : 변수 값이 저장되는 위치. 위치 자체는 위치에 저장된 값과 다른 숫자로 표시됩니다.

함수 매개 변수 : 함수 정의에 선언 된 변수로, 함수에 전달 된 변수를 참조하는 데 사용됩니다.

함수 인수 : 호출자가 함수에 전달하는 함수 외부의 변수입니다.

Object variable : 기본 기본 값이 "object"자체가 아닌 변수이며, 값은 오브젝트의 실제 데이터가 저장되는 메모리의 다른 위치에 대한 포인터 (메모리 위치 값)입니다. 대부분의 고급 언어에서 "포인터"측면은 다양한 상황에서 자동 역 참조를 통해 효과적으로 숨겨집니다.

원시 변수 : 값이 실제 값인 변수. 이 개념조차도 다양한 언어의 자동 복싱 및 객체와 유사한 컨텍스트로 인해 복잡 할 수 있지만 일반적인 아이디어는 변수의 값이 다른 메모리 위치에 대한 포인터가 아니라 변수의 기호로 표시되는 실제 값이라는 것입니다.

함수 인수와 매개 변수는 동일하지 않습니다. 또한 변수의 값은 변수의 객체가 아닙니다 (다양한 사람들이 이미 지적했지만 분명히 무시합니다). 이러한 차이점은 올바른 이해에 중요합니다.

값별 전달 또는 공유 별 호출 (객체 용): 함수 인수의 값은 스택 또는 힙에 있는지 여부에 관계없이 함수의 매개 변수 기호로 참조되는 다른 메모리 위치로 복사됩니다. 즉, 함수 매개 변수가 전달 된 인수 값의 사본을 수신했습니다. AND (필수) 인수 값이 호출 함수에 의해 업데이트 / 변경 / 변경되지 않았습니다. 객체 변수의 값은 객체 자체가 아니라 객체에 대한 포인터이므로 객체 변수를 값으로 전달하면 포인터가 함수 매개 변수 변수에 복사됩니다. 함수 매개 변수의 값은 메모리에서 정확히 동일한 객체를 가리 킵니다. 객체 데이터 자체는 함수 매개 변수를 통해 직접 변경할 수 있지만 함수 인수의 값은 절대로 업데이트되지 않으므로 계속 동일하게 나타납니다.객체의 데이터가 변경되었거나 함수 매개 변수에 다른 객체가 할당 된 경우에도 함수 호출 전체 및 이후에도 객체. 참조 된 객체가 함수 매개 변수 변수를 통해 업데이트 가능하기 때문에 함수 인수가 참조로 전달되었다고 결론을 내릴 수 없습니다.

Call / Pass-by-reference : 함수 인자의 값은 해당 함수 파라미터에 의해 직접 업데이트 될 수 있습니다. 도움이되는 경우 함수 매개 변수는 인수에 대한 효과적인 "별칭"이됩니다. 동일한 메모리 위치에서 동일한 값을 효과적으로 참조합니다. 함수 인수가 객체 변수 인 경우, 함수 매개 변수는 여전히 인수와 동일한 객체를 가리 키기 때문에 객체 데이터를 변경하는 기능은 값에 따른 경우와 다르지 않습니다. 그러나 객체 변수의 경우 함수 매개 변수가 완전히 다른 객체로 설정되면 인수도 다른 객체를 가리 킵니다. 값에 의한 경우에는 발생하지 않습니다.

JavaScript는 참조로 전달되지 않습니다. 자세히 읽으면, 모든 반대 의견이 가치에 의한 의미를 이해하지 못하고 함수 매개 변수를 통해 객체의 데이터를 업데이트하는 기능이 "가치에 의한 가치"와 동의어라는 잘못된 결론을 내릴 것입니다.

객체 복제 / 복사 : 새로운 객체가 생성되고 원본 객체의 데이터가 복사됩니다. 이것은 깊은 사본 또는 얕은 사본 일 수 있지만 요점은 새 객체가 생성된다는 것입니다. 개체의 복사본을 만드는 것은 값에 따른 개념과 별개의 개념입니다. 일부 언어는 클래스 객체와 구조체 등을 구별하며 다른 유형의 변수를 전달하는 데 다른 동작을 가질 수 있습니다. 그러나 JavaScript는 객체 변수를 전달할 때 이와 같은 작업을 자동으로 수행하지 않습니다. 그러나 자동 개체 복제가 없다고해서 참조 별 통과로 변환되는 것은 아닙니다.


4

JavaScript는 기본 유형을 값으로 전달하고 객체 유형을 참조로 전달합니다.

이제 사람들은 "참조로 전달"이 Java 등을 설명하는 올바른 방법인지에 대해 끝없이 울부 짖는 것을 좋아합니다. 실제로 그렇습니다. 요점은 이렇습니다 :

  1. 객체를 전달해도 객체는 복사되지 않습니다.
  2. 함수에 전달 된 객체는 함수로 멤버를 수정할 수 있습니다.
  3. 함수에 전달 된 기본 값은 함수로 수정할 수 없습니다. 사본이 만들어집니다.

내 책에서는 참조로 전달이라고합니다.

- 브라이언 바이은 - 프로그래밍 어떤 언어 참조에 의해 전달된다?


최신 정보

이에 대한 반박은 다음과 같습니다.

JavaScript에는 "참조로 전달"이 없습니다.


@Amy 그것은 값으로 전달을 설명하기 때문에 참조로 전달하지 않습니다. 이 답변은 차이점을 보여주는 좋은 답변입니다. stackoverflow.com/a/3638034/3307720
nasch

@nasch 차이점을 이해합니다. # 1과 # 2는 통과 기준 의미를 설명합니다. # 3은 값별 의미를 설명합니다.
Amy

@Amy 1, 2 및 3은 모두 통과 값과 일치합니다. 참조로 전달하려면 다음도 필요합니다. 4 : 함수 내부의 새 값에 참조를 할당하면 (= 연산자 사용) 함수 외부에서 참조를 다시 할당합니다. 이것은 Javascript의 경우가 아니며 독점적으로 가치를 전달합니다. 객체를 전달할 때 객체에 포인터를 전달하고 해당 포인터를 값으로 전달합니다.
nasch

그것은 일반적으로 "통과 기준"이 의미하는 것이 아닙니다. 당신은 나의 질문을 만족 시켰고, 나는 당신에 동의하지 않습니다. 감사.
Amy

"내 책에는 참조로 전달이라고합니다." – 지금까지 쓰여진 모든 단일 컴파일러 책, 통역사 책, 프로그래밍 언어 이론 책 및 컴퓨터 과학 책에서는 그렇지 않습니다.
Jörg W Mittag

3

이것을 이해하는 간단한 방법은 ...

  • 함수를 호출 할 때 변수 자체가 아닌 인수 변수의 내용 (참조 또는 값)을 전달합니다.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
  • 함수, 매개 변수, 내부 inVar1inVar2, 내용이 전달되는받을 수 있습니다.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
  • inVar2의 참조를 받았 으므로 { prop: 2 }객체 속성 값을 변경할 수 있습니다.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }

3

JavaScript에서 함수에 인수를 전달하는 것은 C에서 포인터 값으로 매개 변수를 전달하는 것과 유사합니다.

/*
The following C program demonstrates how arguments
to JavaScript functions are passed in a way analogous
to pass-by-pointer-value in C. The original JavaScript
test case by @Shog9 follows with the translation of
the code into C. This should make things clear to
those transitioning from C to JavaScript.

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

This produces the output:

10
changed
unchanged
*/

#include <stdio.h>
#include <stdlib.h>

struct obj {
    char *item;
};

void changeStuff(int *num, struct obj *obj1, struct obj *obj2)
{
    // make pointer point to a new memory location
    // holding the new integer value
    int *old_num = num;
    num = malloc(sizeof(int));
    *num = *old_num * 10;
    // make property of structure pointed to by pointer
    // point to the new value
    obj1->item = "changed";
    // make pointer point to a new memory location
    // holding the new structure value
    obj2 = malloc(sizeof(struct obj));
    obj2->item = "changed";
    free(num); // end of scope
    free(obj2); // end of scope
}

int num = 10;
struct obj obj1 = { "unchanged" };
struct obj obj2 = { "unchanged" };

int main()
{
    // pass pointers by value: the pointers
    // will be copied into the argument list
    // of the called function and the copied
    // pointers will point to the same values
    // as the original pointers
    changeStuff(&num, &obj1, &obj2);
    printf("%d\n", num);
    puts(obj1.item);
    puts(obj2.item);
    return 0;
}

1
JavaScript에서는 그렇지 않다고 생각합니다.```javascript var num = 5;
Danail Nachev

@DanailNachev : 기술적으로는 사실이지만 ECMAScript 프리미티브가 아닌 변경 가능한 객체의 경우에만 차이점을 관찰 할 수 있습니다.
Jörg W Mittag

3

프로그래밍 언어 변호사를 위해 ECMAScript 5.1 (최신 버전보다 읽기 쉬운) 섹션을 살펴 보았 으며 ECMAScript 메일 링리스트에서 질문 했습니다.

TL; DR : 모든 것은 가치에 의해 전달되지만 Objects의 속성은 참조이며 Object의 정의는 표준에서 오싹합니다.

인수리스트의 구성

11.2.4 절 "인수 목록"에서는 단 하나의 인수로 구성된 인수 목록을 생성 할 때 다음을 설명합니다.

프로덕션 ArgumentList : AssignmentExpression은 다음과 같이 평가됩니다.

  1. AssignmentExpression을 평가 한 결과로 간주하십시오.
  2. arg를 GetValue (ref)로 둡니다.
  3. 유일한 항목이 arg 인 List를 반환합니다.

이 섹션은 인수 목록에 0 또는> 1 개의 인수가있는 경우도 열거합니다.

따라서 모든 것이 참조로 전달됩니다.

객체 속성 액세스

섹션 11.2.1 "속성 접근 자"

프로덕션 MemberExpression : MemberExpression [Expression]은 다음과 같이 평가됩니다.

  1. baseReference를 MemberExpression을 평가 한 결과로 둡니다.
  2. baseValue를 GetValue (baseReference)로 설정하십시오.
  3. propertyNameReference를 Expression을 평가 한 결과로 둡니다.
  4. propertyNameValue를 GetValue (propertyNameReference)로 설정하십시오.
  5. CheckObjectCoercible (baseValue)을 호출하십시오.
  6. propertyNameString을 ToString (propertyNameValue)으로 설정하십시오.
  7. 평가중인 구문 작성이 엄격 모드 코드에 포함되어 있으면 엄격을 true로 설정하고 그렇지 않으면 엄격을 false로 설정하십시오.
  8. 기본 값이 baseValue이고 참조 이름이 propertyNameString이고 엄격 모드 플래그가 엄격한 Reference 유형의 값을 리턴합니다 .

따라서 객체의 속성은 항상 참조로 사용할 수 있습니다.

참조

8.7 "참조 사양 유형"섹션에 설명되어 있으며, 참조는 언어의 실제 유형이 아니며 삭제, typeof 및 대입 연산자의 동작을 설명하는 데만 사용됩니다.

"객체"의 정의

"개체는 속성의 모음"이라고 5.1 판에 정의되어 있습니다. 그러므로 우리는 객체의 가치가 콜렉션이라고 추론 할 수 있지만, 콜렉션의 가치는 스펙에 잘 정의되어 있지 않으며 이해 하기 위해 약간의 노력 이 필요합니다 .


가치에 의해 전달 된 인수, 참조에 의해 전달 된 인수, 전체 객체에 대한 연산 및 속성에 대한 연산 사이의 차이로 인해 얼마나 많은 사람들이 혼란스러워하는지 결코 끝나지 않습니다. 1979 년에는 컴퓨터 과학 학위를받지 못하여 MBA 프로그램에 15 시간 정도의 CS 선택 과목을 추가하기로 결정했습니다. 그럼에도 불구하고,이 개념에 대한 나의 이해는 최소한 컴퓨터 과학이나 수학 학위를 가진 동료들에 의해 잡힌 것만큼이나 훌륭하다는 것이 곧 분명해졌습니다. 어셈블러를 공부하면 분명해질 것입니다.
David A. Grey

3

MDN 문서는 너무 자세하지 않고 명확하게 설명합니다.

함수 호출의 매개 변수는 함수의 인수 입니다. 인수는 value별로 함수 전달됩니다 . 함수가 인수의 값을 변경하면이 변경 사항이 전체적으로 또는 호출 함수에 반영되지 않습니다. 그러나 객체 참조도 값이며 특수합니다. 함수가 참조 된 객체의 속성을 변경하면 해당 변경 내용은 함수 외부에서 볼 수 있습니다 (...)

출처 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description


1

저수준 언어에서 참조로 변수를 전달하려면 함수 작성시 특정 구문을 사용해야합니다.

int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
  *age = *age + 1;
}

&age에 대한 참조입니다 myAge,하지만 당신은 값을 원하는 경우 사용하여 참조를 변환해야합니다 *age.

Javascript는 이러한 변환을 수행하는 고급 언어입니다. 따라서 객체는 참조로 전달되지만 언어는 참조 매개 변수를 값으로 변환합니다. 당신은 사용하지 할 필요가 없습니다 &, 함수 정의에 참조를 전달하는, 둘 *, JS는 않습니다 당신을 위해 값에 대한 참조를 변환, 함수 본문에.

그렇기 때문에 함수 내에서 객체의 값을 바꾸려고 할 때 (예 age = {value:5}:) 변경 사항이 유지되지 않지만 속성 (예 :)을 변경하면 변경됩니다 age.value = 5.

더 알아보기


1

이 답변을 여러 번 읽었지만 Barbara Liskov가 말하는 "공유하여 전화" 의 기술적 정의에 대해 배울 때까지는 실제로 그것을 얻지 못했습니다.

공유에 의한 호출의 의미는 함수 내의 함수 인수에 대한 할당이 (참조 의미론과 달리) 호출자에게 표시되지 않는다는 점에서 참조에 의한 호출과 다릅니다. 예를 들어 변수가 전달 된 경우 불가능합니다 호출자의 범위에서 해당 변수에 대한 할당을 시뮬레이션합니다. 그러나 함수가 호출자와 동일한 오브젝트에 액세스 할 수 있으므로 (복사되지 않음), 오브젝트가 변경 가능한 경우 함수 내에서 호출자가 볼 수 있으며, 이는 값에 의한 호출과 다를 수 있습니다. 의미론. 함수 내에서 변경 가능한 객체의 돌연변이는 객체가 복사 또는 복제되지 않기 때문에 호출자가 볼 수 있습니다.

즉, 매개 변수 값 자체에 액세스하면 매개 변수 참조를 변경할 수 있습니다. 반면, 평가 후 매개 변수에 대한 지정은 사라지고 함수 호출자는 액세스 할 수 없습니다.


아니요, 객체가 변경 가능한지 여부는 실제로 문제가되지 않습니다. 모든 것은 항상 가치에 의해 전달됩니다. 그것은 당신이 전달하는 것 (값 또는 참조)에 달려 있습니다. 참조 .
Scott Marcus

그녀가 설명하는 것은 참조 BY-VALUE를 전달하는 것입니다. 새로운 용어를 도입 할 이유가 없습니다.
Sanjeev

1

가장 간단한 방법

// Copy JS object without reference
var first = {"a":"value1","b":"value2"};
var clone = JSON.parse( JSON.stringify( first ) ); 

var second = ["a","b","c"];
var clone = JSON.parse( JSON.stringify( second ) ); 

JSON.parse( JSON.stringify( obj ) )개체를 딥 복제하는 끔찍한 방법입니다. 속도가 느릴뿐만 아니라 데이터가 손실 될 수도 있습니다.
D. Pardal

0

I는 발견 연장 방법Underscore.js 라이브러리 나도 전적으로 변형 또는 대체 될 수있는 매개 변수로서 객체를 전달하고자 할 때 매우 유용하다.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}

0

내가 찾은 가장 간결한 설명은 AirBNB 스타일 가이드에 있습니다 .

  • 프리미티브 : 프리미티브 유형에 액세스하면 해당 값을 직접 작업합니다.

    • 번호
    • 부울
    • 없는
    • 찾으시는 주소가 없습니다

예 :

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Complex : 복합 유형에 액세스하면 해당 값에 대한 참조 작업

    • 목적
    • 정렬
    • 함수

예 :

var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

즉, 효과적으로 기본 유형은 값으로 전달되고 복합 유형은 참조로 전달됩니다.


아니요, 모든 것이 항상 가치에 의해 전달됩니다. 그것은 당신이 전달하는 것 (값 또는 참조)에 달려 있습니다. 참조 .
Scott Marcus

-1

나는 그것이 패스 바이 패스라고 말할 것입니다-

인수와 변수 객체는 함수 호출의 시작 부분에서 생성 된 실행 컨텍스트 동안 생성 된 객체입니다. 함수에 전달 된 실제 값 / 참조는이 인수 + 변수 객체에 저장됩니다.

간단히 말해 기본 유형의 경우 함수 호출의 시작 부분에서 값이 복사되고 객체 유형의 경우 참조가 복사됩니다.


1
"복사하여 전달"=== 값으로 전달
Scott Marcus

-1

JavaScript에서 "pass by reference"라는 용어를 사용하는 방법에 대한 토론이 있지만 여기 에 귀하의 질문에 대한 답변이 있습니다.

명시 적으로 언급 할 필요없이 객체는 참조에 의해 자동으로 전달됩니다.

(위의 기사에서)


7
링크 된 기사는 더 이상 해당 진술을 포함하지 않으며 "통과 기준"을 사용하지 않습니다.
C Perkins

값은 참조입니다

-2

어떤 것이 "통과 기준"인지를 결정하는 쉬운 방법은 "스왑"기능을 작성할 수 있는지 여부입니다. 예를 들어 C에서는 다음을 수행 할 수 있습니다.

void swap(int *i, int *j)
{
    int t;
    t = *i;
    *i = *j;
    *j = t;
}

JavaScript에서 이와 동등한 기능을 수행 할 수없는 경우 "pass by reference"가 아닙니다.


21
이것은 실제로 참조로 전달되지 않습니다. 함수에 포인터를 전달하고 해당 포인터를 값으로 전달합니다. 더 좋은 예는 C ++ 및 연산자 또는 C #의 "ref"키워드이며 둘 다 참조로 전달됩니다.
Matt Greer

더 쉬운 것은 모든 것이 JavaScript에서 가치로 전달된다는 것입니다.
Scott Marcus


-3
  1. 문자열, 숫자와 같은 기본 유형 변수는 항상 값으로 전달됩니다.
  2. 이 두 가지 조건에 따라 배열과 객체는 참조로 전달되거나 값으로 전달됩니다.

    • 새 객체 또는 배열로 해당 객체 또는 배열의 값을 변경하는 경우 값을 전달합니다.

      object1 = {item: "car"}; array1=[1,2,3];

    여기에서 새 객체 또는 배열을 이전 객체에 할당합니다. 오래된 객체의 속성 값을 변경하지 않으므로 값으로 전달됩니다.

    • 객체 또는 배열의 속성 값을 변경하는 경우 참조를 통해 전달됩니다.

      object1.key1= "car"; array1[0]=9;

    여기에서 이전 객체의 속성 값을 변경합니다. 이전 객체에 새 객체 또는 배열을 할당하지 않으므로 참조로 전달됩니다.

암호

    function passVar(object1, object2, number1) {

        object1.key1= "laptop";
        object2 = {
            key2: "computer"
        };
        number1 = number1 + 1;
    }

    var object1 = {
        key1: "car"
    };
    var object2 = {
        key2: "bike"
    };
    var number1 = 10;

    passVar(object1, object2, number1);
    console.log(object1.key1);
    console.log(object2.key2);
    console.log(number1);

Output: -
    laptop
    bike
    10

1
대입 연산자를 함수 호출과 혼동하지 마십시오. 기존 변수에 새 데이터를 할당하면 이전 데이터의 참조 수가 감소하고 새 데이터가 이전 변수와 연관됩니다. 기본적으로 변수는 새 데이터를 가리 킵니다. 속성 변수도 마찬가지입니다. 이러한 할당은 함수 호출이 아니기 때문에 값별 또는 참조 별과는 아무런 관련이 없습니다.
John Sonderson

1
아니요, 모든 것이 항상 가치에 의해 전달됩니다. 그것은 당신이 전달하는 것 (값 또는 참조)에 달려 있습니다. 참조 .
Scott Marcus

-3
  1. 프리미티브 (숫자, 부울 등)는 값으로 전달됩니다.
    • 문자열은 변경할 수 없으므로 실제로 중요하지 않습니다.
  2. 객체는 참조로 전달됩니다 (참조는 값으로 전달).

아니요, 모든 것이 항상 가치에 의해 전달됩니다. 그것은 당신이 전달하는 것 (값 또는 참조)에 달려 있습니다. 참조 .
Scott Marcus

두 번째 진술은 모순됩니다.
Jörg W Mittag

-5

함수 내부의 단순한 값은 함수 외부의 값을 변경하지 않지만 (값으로 전달됨) 복잡한 값은 참조로 전달됩니다.

function willNotChange(x) {

    x = 1;
}

var x = 1000;

willNotChange(x);

document.write('After function call, x = ' + x + '<br>'); // Still 1000

function willChange(y) {

    y.num = 2;
}

var y = {num: 2000};

willChange(y);
document.write('After function call y.num = ' + y.num + '<br>'); // Now 2, not 2000

즉, 기능 수준 범위로 인해 y가 변경되며 참조로 전달되지 않기 때문에 게양됩니다.
Parijat Kalia

아니요, 모든 것이 항상 가치에 의해 전달됩니다. 그것은 당신이 전달하는 것 (값 또는 참조)에 달려 있습니다. 참조 .
Scott Marcus
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.