자바 스크립트에서 참조로 변수 전달


285

JavaScript에서 참조로 변수를 전달하는 방법은 무엇입니까? 여러 작업을 수행하려는 3 개의 변수가 있으므로 for 루프에 넣고 각 작업을 수행하려고합니다.

의사 코드 :

myArray = new Array(var1, var2, var3);
for (var x = 0; x < myArray.length; x++){
    //do stuff to the array
    makePretty(myArray[x]);
}
//now do stuff to the updated vars

가장 좋은 방법은 무엇입니까?


31
'참조로 전달'에 대해 이야기하고 있지만 예제에는 함수 호출이 없으므로 예제에는 전혀 전달되지 않습니다. 무엇을하려고하는지 명확히하십시오.
jfriend00

1
혼란을 드려 죄송합니다. 특별히 함수를 작성할 필요가 없었으므로 'pass by reference'는 단어를 잘못 선택했습니다. 글을 쓰지 않고 변수에 대해 몇 가지 연산을 수행하고 싶습니다. makePretty(var1); makePretty(var2); makePretty(var3); ...
BFTrick

귀하의 의견에 따라 : arr = [var1, var2, var3]; for (var i = 0, len = arr.length; i < len; i++) { arr[i] = makePretty(arr[i]); }-당신 makePretty은 배열의 슬롯에 다시 반환 된 값을 저장 하면됩니다.
dylnmc

답변:


415

JavaScript에는 "참조로 전달"이 없습니다. 객체를 전달하면 (즉, 객체에 대한 값을 기준으로 전달할 수 있음) 함수가 객체 내용을 수정하도록 할 수 있습니다.

function alterObject(obj) {
  obj.foo = "goodbye";
}

var myObj = { foo: "hello world" };

alterObject(myObj);

alert(myObj.foo); // "goodbye" instead of "hello world"

숫자 인덱스를 사용하여 배열의 속성을 반복하고 원하는 경우 배열의 각 셀을 수정할 수 있습니다.

var arr = [1, 2, 3];

for (var i = 0; i < arr.length; i++) { 
    arr[i] = arr[i] + 1; 
}

"by-by-by-reference"는 매우 구체적인 용어입니다. 단순히 수정 가능한 객체에 대한 참조를 전달할 수 있다는 의미는 아닙니다. 대신, 함수가 호출 컨텍스트 에서 해당 값을 수정할 수 있도록 간단한 변수를 전달할 수 있음을 의미합니다 . 그래서:

 function swap(a, b) {
   var tmp = a;
   a = b;
   b = tmp; //assign tmp to b
 }

 var x = 1, y = 2;
 swap(x, y);

 alert("x is " + x + ", y is " + y); // "x is 1, y is 2"

C ++과 같은 언어 에서는 언어 참조 기준 을 가지기 때문에 그렇게 할 수 있습니다 .

편집 -최근 (2015 년 3 월) 아래에서 언급 한 것과 유사한 블로그 게시물을 통해 Reddit을 다시 살펴 보았습니다. Reddit에서 앞뒤로 읽는 동안 혼란의 큰 부분은 "참조"라는 단어와 관련된 불행한 충돌에서 비롯된 것입니다. "참조로 전달"및 "값으로 전달"이라는 용어는 프로그래밍 언어에서 "개체"를 다루는 개념보다 우선합니다. 그것은 실제로 객체에 관한 것이 아닙니다. 함수 매개 변수, 특히 함수 매개 변수가 호출 환경에 "연결된"방법에 관한 것입니다. 특히,JavaScript에서와 똑같이 보입니다. 그러나 호출 환경에서 객체 참조를 수정할 수도 있으며 이것이 JavaScript에서 수행 할 수없는 핵심 사항입니다 . 참조 별 언어는 참조 자체가 아니라 참조에 대한 참조를 전달 합니다.

편집 - 여기 주제에 대한 블로그 게시물이 있습니다. (이 게시물에 대한 주석은 C ++에 실제로 참조를 통한 전달이 없음을 설명합니다. 사실입니다. 그러나 C ++의 기능은 함수 시점에서 명시 적으로 일반 변수에 대한 참조를 생성하는 기능입니다 포인터를 생성하기위한 호출 또는 인수 유형 서명이 수행되어야하는 함수를 호출 할 때 암시 적으로 호출합니다. JavaScript가 지원하지 않는 주요 기능입니다.)


8
OP가 실제로 요구하는 것으로 생각되는 원래 객체 또는 배열을 변경할 수있는 객체 또는 배열에 대한 참조를 전달할 수 있습니다.
jfriend00

2
글쎄, OP는 용어를 사용했지만 실제 코드는 전혀 "통과"를 포함하지 않는 것 같습니다 :-) 나는 그가 무엇을하려고하는지 확실하지 않습니다.
Pointy

5
참조를 전달 값은 전달과 동일하지 참조 는 이와 같은 어떤 시나리오 때문에 나타날 수 있지만.
Travis Webb

5
귀하의 블로거는 C ++에 대한 잘못된 않습니다 통과하여 참조를하고, 자신의 게시물은 이해되지 않는다. 초기화 후 참조 값을 변경할 수 없다는 것은 관련이 없습니다. 'pass by reference'와는 아무런 관련이 없습니다. '참조를 통한 통과'시맨틱은 C #을 통해 다시 추적 할 수있다 '는 그의 말에 경보 벨이 울렸다.
EML

7
@Pointy 이것은 끔찍한 대답입니다. 도움이 필요하면 마지막으로 누군가가 의미 론적으로 학교를 다니는 것입니다. "기준 별 통과"는 단순히 "함수가 어느 정도 인수로 전달 된 변수의 값을 변경할 수 있음"을 의미합니다. (질문과 관련하여) 실제로 그렇게 복잡하지는 않습니다.
crownlessking

109
  1. 문자열 및 숫자와 같은 기본 유형 변수는 항상 값으로 전달됩니다.
  2. 배열과 객체는 다음 조건에 따라 참조 또는 값으로 전달됩니다.

    • 객체 또는 배열의 값을 설정하는 경우 Pass by Value입니다.

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

    • 객체 또는 배열의 속성 값을 변경하는 경우 참조로 전달입니다.

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

암호

function passVar(obj1, obj2, num) {
    obj1.prop = "laptop"; // will CHANGE original
    obj2 = { prop: "computer" }; //will NOT affect original
    num = num + 1; // will NOT affect original
}

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

passVar(object1, object2, number1);
console.log(object1); //output: Object {item:"laptop"}
console.log(object2); //output: Object {item:"bike"}
console.log(number1); //ouput: 10


21
이것이 "통과 기준"의 의미가 아닙니다. 이 용어는 실제로 객체와는 관련이 없지만 호출 된 함수의 매개 변수와 호출 환경의 변수 간의 관계와 관련이 있습니다.
Pointy

12
모든 것은 항상 가치에 의해 전달됩니다. 프리미티브가 아닌 경우 전달되는 값은 참조입니다. 참조에 지정하면 해당 참조가 자연스럽게 값이므로 변경되어 원래 참조 된 다른 객체를 변경할 수 없습니다. 참조로 지정된 객체를 변경하면 지정된 객체를 변경하여 원하는대로 정확하게 작동합니다. 두 시나리오 모두 완벽하게 일관성이 있으며, 시간이 지남에 따라 JavaScript가 함수로 전달되는 방식을 변경하여 JavaScript 작동 방식을 마술처럼 변경하지도 않습니다.
Matthew 읽기

9
이 답변은 이전 답변을 명확하게 설명했지만, 더 많은 투표권을 가지고 있지만 (이 시점에서 287 건의 승인을 받았음에도 불구하고) JS에서 참조로 실제로 통과시키는 방법에 대해서는 명확하지 않습니다. 요컨대, 이 답변의 코드는 효과가 있었고 더 많은 투표를 모은 답변은 그렇지 않았습니다.
Pap

25

참조로 변수를 전달하는 해결 방법 :

var a = 1;
inc = function(variableName) {
  window[variableName] += 1;
};

inc('a');

alert(a); // 2


편집하다

,, 실제로 당신은 글로벌 액세스없이 할 수 있습니다

inc = (function () {
    var variableName = 0;

    var init = function () {
        variableName += 1;
        alert(variableName);
    }

    return init;
})();

inc();

@ 필자는 전역 / 창 값에주의를 기울이는 것이 좋지만 브라우저에서 수행하는 모든 작업은 창 개체의 자식 또는 자손입니다. nodejs에서 모든 것은 GLOBAL의 자손입니다. 컴파일 된 객체 언어에서는 명시 적 상위 객체가 아닌 경우 암시 적입니다. 그렇지 않으면 힙 관리가 더 복잡해집니다 (그리고 무엇을 위해)?
dkloke

1
@ dkloke : 예, 결국 jQuery는 window. $ / window.jQuery를 사용하고 다른 방법은 아래에있는 것처럼 창 객체를 터치해야합니다. 나는 통합 네임 스페이스 아래에 변수를두기보다는 많은 변수를 추가하는 글로벌 네임 스페이스의 오염에 대해 이야기했습니다.
Phil

2
나는 그 접근법이 얼마나 나쁜지를 나타내는 좋은 단어를 찾을 수 없다; (
SOReader

참조로 변수를 전달하지 않아도 마지막 솔루션 (편집 된 버전)을 좋아합니다. 변수에 직접 액세스 할 수 있기 때문에 이점이 있습니다.
John Boe

11

간단한 객체

var ref = { value: 1 };

function Foo(x) {
    x.value++;
}

Foo(ref);
Foo(ref);

alert(ref.value); // Alert: 3

커스텀 객체

목적 rvar

function rvar (name, value, context) {
    if (this instanceof rvar) {
        this.value = value;
        Object.defineProperty(this, 'name', { value: name });
        Object.defineProperty(this, 'hasValue', { get: function () { return this.value !== undefined; } });
        if ((value !== undefined) && (value !== null))
            this.constructor = value.constructor;
        this.toString = function () { return this.value + ''; };
    } else {
        if (!rvar.refs)
            rvar.refs = {};
        if (!context)
            context = window;
        // Private
        rvar.refs[name] = new rvar(name, value);
        // Public
        Object.defineProperty(context, name, {
            get: function () { return rvar.refs[name]; },
            set: function (v) { rvar.refs[name].value = v; },
            configurable: true
        });

        return context[name];
    }
}

변수 선언

rvar('test_ref');
test_ref = 5; // test_ref.value = 5

또는:

rvar('test_ref', 5); // test_ref.value = 5

테스트 코드

rvar('test_ref_number');
test_ref_number = 5;
function Fn1 (v) { v.value = 100; }
console.log("rvar('test_ref_number');");
console.log("test_ref_number = 5;");
console.log("function Fn1 (v) { v.value = 100; }");
console.log('test_ref_number.value === 5', test_ref_number.value === 5);
console.log(" ");

Fn1(test_ref_number);
console.log("Fn1(test_ref_number);");
console.log('test_ref_number.value === 100', test_ref_number.value === 100);
console.log(" ");

test_ref_number++;
console.log("test_ref_number++;");
console.log('test_ref_number.value === 101', test_ref_number.value === 101);
console.log(" ");

test_ref_number = test_ref_number - 10;
console.log("test_ref_number = test_ref_number - 10;");
console.log('test_ref_number.value === 91', test_ref_number.value === 91);

console.log(" ");
console.log("---------");
console.log(" ");

rvar('test_ref_str', 'a');
console.log("rvar('test_ref_str', 'a');");
console.log('test_ref_str.value === "a"', test_ref_str.value === 'a');
console.log(" ");

test_ref_str += 'bc';
console.log("test_ref_str += 'bc';");
console.log('test_ref_str.value === "abc"', test_ref_str.value === 'abc');

테스트 콘솔 결과

rvar('test_ref_number');
test_ref_number = 5;
function Fn1 (v) { v.value = 100; }
test_ref_number.value === 5 true

Fn1(test_ref_number);
test_ref_number.value === 100 true

test_ref_number++;
test_ref_number.value === 101 true

test_ref_number = test_ref_number - 10;
test_ref_number.value === 91 true

---------

rvar('test_ref_str', 'a');
test_ref_str.value === "a" true

test_ref_str += 'bc';
test_ref_str.value === "abc" true 

5

참조에 의해 (로컬, 프리미티브) 변수를 전달하는 또 다른 방법은 변수를 "바로"클로저로 래핑하는 것 eval입니다. "엄격한 사용"에서도 작동합니다. (참고 : evalJS 옵티 마이저에게는 친숙하지 않으며 변수 이름 주위에 따옴표가 없으면 예기치 않은 결과가 발생할 수 있습니다)

"use strict"

//return text that will reference variable by name (by capturing that variable to closure)
function byRef(varName){
    return "({get value(){return "+varName+";}, set value(v){"+varName+"=v;}})";
}

//demo

//assign argument by reference
function modifyArgument(argRef, multiplier){
    argRef.value = argRef.value * multiplier;
}

(function(){

var x = 10;

alert("x before: " + x);
modifyArgument(eval(byRef("x")), 42);
alert("x after: " + x);

})()

라이브 샘플 https://jsfiddle.net/t3k4403w/


이것은 대단합니다
hard_working_ant

3

실제로 꽤 해결책이 있습니다.

function updateArray(context, targetName, callback) {
    context[targetName] = context[targetName].map(callback);
}

var myArray = ['a', 'b', 'c'];
updateArray(this, 'myArray', item => {return '_' + item});

console.log(myArray); //(3) ["_a", "_b", "_c"]

3

다양한 프로그래밍 언어에서 제공하는 "참조로 전달"기능을 개인적으로 싫어합니다. 아마도 그것은 함수형 프로그래밍의 개념을 발견하기 때문일 수도 있지만 부작용을 일으키는 함수 (참조로 전달되는 매개 변수 조작과 같은)를 볼 때 항상 구즈 범프를 얻습니다. 나는 개인적으로 "단일 책임"원칙을 강하게 받아들입니다.

IMHO, 함수는 return 키워드를 사용하여 하나의 결과 / 값만 반환해야합니다. 매개 변수 / 인수를 수정하는 대신 수정 된 매개 변수 / 인수 값을 반환하고 원하는 재 할당을 호출 코드까지 남겨 두었습니다.

그러나 때로는 (아주 드물게) 동일한 함수에서 두 개 이상의 결과 값을 반환해야합니다. 이 경우 모든 결과 값을 단일 구조 또는 객체에 포함하도록 선택합니다. 다시 할당을 처리하는 것은 호출 코드에 달려 있습니다.

예:

인수 목록에서 'ref'와 같은 특수 키워드를 사용하여 전달 매개 변수를 지원한다고 가정하십시오. 내 코드는 다음과 같습니다.

//The Function
function doSomething(ref value) {
    value = "Bar";
}

//The Calling Code
var value = "Foo";
doSomething(value);
console.log(value); //Bar

대신 실제로 다음과 같은 작업을 선호합니다.

//The Function
function doSomething(value) {
    value = "Bar";
    return value;
}

//The Calling Code:
var value = "Foo";
value = doSomething(value); //Reassignment
console.log(value); //Bar

여러 값을 반환하는 함수를 작성해야 할 때 참조로 전달 된 매개 변수도 사용하지 않습니다. 따라서 다음과 같은 코드를 피할 것입니다.

//The Function
function doSomething(ref value) {
    value = "Bar";

    //Do other work
    var otherValue = "Something else";

    return otherValue;
}

//The Calling Code
var value = "Foo";
var otherValue = doSomething(value);
console.log(value); //Bar
console.log(otherValue); //Something else

대신 실제로 객체 내부에 다음과 같이 두 가지 새로운 값을 반환하는 것을 선호합니다.

//The Function
function doSomething(value) {
    value = "Bar";

    //Do more work
    var otherValue = "Something else";

    return {
        value: value,
        otherValue: otherValue
    };
}

//The Calling Code:
var value = "Foo";
var result = doSomething(value);
value = result.value; //Reassignment
console.log(value); //Bar
console.log(result.otherValue);

이 코드 예제는 매우 간단하지만 개인적으로 이러한 작업을 처리하는 방법을 대략적으로 보여줍니다. 올바른 장소에서 다양한 책임을 지도록 도와줍니다.

행복한 코딩. :)


물건을 할당하고 다시 할당하는 것은 (유형 검사없이) 구스 범프를 제공합니다. 책임에 관해서는 doSomething ()이 그 일을 할 것으로 예상합니다. 검색 해야하는 배열이 있다고 가정 해보십시오. 두 번째 배열에 일치하는 항목을 배치하고 얼마나 많은 항목을 찾았는지 알고 싶습니다. 표준 솔루션은 다음과 같은 함수를 호출하는 것입니다. 'var foundArray; if ((findStuff (inArray, & foundArray))> 1) {// 프로세스 foundArray} '. 이 시나리오의 어느 곳에서도 바람직하지 않은 새로운 알 수없는 객체가 있습니다.
엘리스 반 루이

@ElisevanLooij 예제의 경우 findStuff결과 배열을 간단히 반환하는 것이 좋습니다. foundArray변수도 선언해야 하므로 결과 배열을 변수에 직접 할당합니다 var foundArray = findStuff(inArray); if (foundArray.length > 0) { /* process foundArray */ }. 이것은 1) 호출 코드를 더 읽기 쉽고 이해하기 쉽게 만들고 2) findStuff메소드 의 내부 기능 (및 테스트 가능성)을 상당히 단순화하여 실제로 다른 (사례) 시나리오 / 시나리오에서 훨씬 더 유연합니다.
Bart Hofland

@ElisevanLooij 그러나, 나는 (내 대답 에서처럼) 재 할당이 실제로 "코드 냄새"이며 실제로 가능한 한 많은 것을 피하고 싶다는 데 동의합니다. 나는 주제에 대한 나의 실제 의견을 더 잘 반영 할 수있는 방식으로 나의 답변을 편집 (또는 재구성)하는 것에 대해 생각할 것이다. 당신의 반응에 감사드립니다. :)
Bart Hofland

2

나는 이런 종류의 일을하기 위해 구문을 가지고 놀고 있지만 약간 특이한 도우미가 필요합니다. 'var'을 사용하지 않고 로컬 변수를 만들고 익명 콜백을 통해 범위를 정의하는 간단한 'DECLARE'도우미로 시작합니다. 변수 선언 방법을 제어하여 변수를 객체에 래핑하여 기본적으로 항상 참조로 전달할 수 있습니다. 이것은 위의 Eduardo Cuomo의 답변 중 하나와 비슷하지만 아래 솔루션에는 문자열을 변수 식별자로 사용할 필요가 없습니다. 개념을 보여주는 최소한의 코드가 있습니다.

function Wrapper(val){
    this.VAL = val;
}
Wrapper.prototype.toString = function(){
    return this.VAL.toString();
}

function DECLARE(val, callback){
    var valWrapped = new Wrapper(val);    
    callback(valWrapped);
}

function INC(ref){
    if(ref && ref.hasOwnProperty('VAL')){
        ref.VAL++; 
    }
    else{
        ref++;//or maybe throw here instead?
    }

    return ref;
}

DECLARE(5, function(five){ //consider this line the same as 'let five = 5'
console.log("five is now " + five);
INC(five); // increment
console.log("five is incremented to " + five);
});

2

실제로는 정말 쉽습니다.

문제는 일단 고전적인 인수를 전달하면 다른 읽기 전용 영역 으로 범위가 지정된다는 것을 이해하는 것입니다 .

해결책은 JavaScript의 객체 지향 디자인을 사용하여 인수를 전달하는 것입니다.

args를 전역 / 범위 변수에 넣는 것과 동일하지만 더 좋습니다 ...

function action(){
  /* process this.arg, modification allowed */
}

action.arg = [ ["empty-array"],"some string",0x100,"last argument" ];
action();

잘 알려진 체인을 즐기기 위해 물건을 약속 할 수도 있습니다. 약속 구조와 같은 모든 것이 있습니다.

function action(){
  /* process this.arg, modification allowed */
  this.arg = ["a","b"];
}

action.setArg = function(){this.arg = arguments; return this;}

action.setArg(["empty-array"],"some string",0x100,"last argument")()

또는 더 나은 아직 .. action.setArg(["empty-array"],"some string",0x100,"last argument").call()


this.argaction인스턴스 에서만 작동 합니다. 작동하지 않습니다.
Eduardo Cuomo

1

Javascript는 함수 내부의 배열 항목을 수정할 수 있습니다 (객체 / 배열에 대한 참조로 전달됨).

function makeAllPretty(items) {
   for (var x = 0; x < myArray.length; x++){
      //do stuff to the array
      items[x] = makePretty(items[x]);
   }
}

myArray = new Array(var1, var2, var3);
makeAllPretty(myArray);

또 다른 예는 다음과 같습니다.

function inc(items) {
  for (let i=0; i < items.length; i++) {
    items[i]++;
  }
}

let values = [1,2,3];
inc(values);
console.log(values);
// prints [2,3,4]

1

JS는 강력한 유형이 아니기 때문에이 트레드와 같이 여러 가지 방법으로 문제를 해결할 수 있습니다.

그러나 유지 관리 측면에서 Bart Hofland에 동의해야합니다. 함수는 인수로 무언가를 수행하고 결과를 반환해야합니다. 쉽게 재사용 할 수 있습니다.

변수를 참조로 전달해야한다고 생각하면 IMHO 객체로 변수를 작성하는 것이 더 좋습니다.


1

통과 기준 토론을 제외하고, 언급 된 질문에 대한 해결책을 찾고있는 사람들은 다음을 사용할 수 있습니다.

const myArray = new Array(var1, var2, var3);
myArray.forEach(var => var = makePretty(var));

0

무슨 말인지 정확히 알아 Swift에서도 마찬가지입니다. 결론은 사용 let하지 않습니다 var.

프리미티브는 값에 의해 전달되지만 var i반복 시점의 값이 익명 함수에 복사되지 않는다는 사실은 가장 놀랍습니다.

for (let i = 0; i < boxArray.length; i++) {
  boxArray[i].onclick = function() { console.log(i) }; // correctly prints the index
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.