JavaScriptで変数を参照渡しする


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
混乱させて申し訳ありません。特に関数を書く必要はなかったので、「参照渡し」は単語の選択としては不十分でした。私は変数にいくつかの操作を記述せずに実行できるようにしたいだけです 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 2017年

回答:


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; 
}

「参照渡し」は非常に具体的な用語であることに注意することが重要です。変更可能なオブジェクトへの参照を渡すことが単に可能であるという意味ではありません。代わりに、関数が呼び出しコンテキストでその値を変更できるように、単純な変数を渡すことが可能であることを意味します。そう:

 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月)、下記の私のブログに似ていますが、この場合はJavaに関するものです。Redditのコメントで前後を読んでいるときに、混乱の大部分が「参照」という単語が関係する不幸な衝突に起因していることがわかりました。「参照渡し」および「値渡し」という用語は、プログラミング言語で「オブジェクト」を操作するという概念よりも古いものです。本当にオブジェクトについてではありません。それは関数パラメーターについてであり、具体的には関数パラメーターが呼び出し環境に「接続」されている(または接続されていない)方法です。特に、、JavaScriptとほぼ同じように見えます。ただし、呼び出し環境でオブジェクト参照を変更することできます。これは、JavaScript では実行できない重要なことです。参照渡し言語は、参照自体ではなく、参照への参照を渡します

編集 —これはトピックに関するブログ投稿です。(C ++には実際には参照渡しがないことを説明しているその投稿へのコメントに注意してください。それは事実です。しかし、C ++には、関数のポイントで明示的にプレーン変数への参照を作成する機能がありますポインタを作成するための呼び出し、または引数の型シグネチャがそれを実行することを要求する関数を呼び出すときに暗黙的に。これらは、JavaScriptがサポートしていない主要なものです。)


8
オブジェクトまたは配列への参照を渡すことができます。これにより、OPが実際に要求しているものであると私が信じている元のオブジェクトまたは配列を変更できます。
jfriend00

2
まあ、OPはこの用語を使用しましたが、実際のコードには「パス」がまったく含まれていないようです。
先のとがった

5
参照を渡す値によっては通過と同じではない参照することにより、それはこのようないくつかのシナリオではそう見えるかもしれません。
Travis Webb

5
あなたのブロガーは、参照渡しを行う C ++について間違っており、彼の投稿は意味がありません。初期化後に参照の値を変更できないことは関係ありません。これは「参照渡し」とは関係ありません。「「参照渡し」セマンティクスはC#をたどって追跡できる」という彼の発言は、警報ベルを鳴らすべきだった。
EML

7
@Pointyこれは恐ろしい答えです。私が助けを必要とするなら、私が望んでいる最後のことは意味論で私を教育している誰かです。「参照渡し」とは、単に、「関数が、引数として渡された変数の値をある程度変更できること」を意味します。(質問の文脈で)それはあなたがそれを理解するほど実際には複雑ではありません。
Crownlessking 2017年

108
  1. 文字列や数値などのプリミティブ型の変数は、常に値で渡されます。
  2. 配列とオブジェクトは、次の条件に基づいて、参照または値によって渡されます。

    • オブジェクトまたは配列の値を設定する場合は、値渡しです。

      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の動作を魔法のように変更するために、時間をさかのぼり、関数に渡す方法を変更します。
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();

@Philグローバル/ウィンドウの値に注意するのは良いことですが、ある時点で、ブラウザーで実行していることはすべて、ウィンドウオブジェクトの子または子孫です。nodejsでは、すべてがGLOBALの子孫です。コンパイルされたオブジェクト言語では、明示的な親オブジェクトでなくても暗黙的です。そうしないと、ヒープ管理が複雑になります(そして何のために?)。
dkloke 2015年

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です。これは、「use strict」でも機能します。(注: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"]

2

私はこの種のことをするために構文をいじっていますが、少し変わったいくつかのヘルパーが必要です。それは、「var」をまったく使用しないことから始まりますが、ローカル変数を作成し、匿名コールバックを介してそのスコープを定義する単純な「DECLARE」ヘルパーです。変数の宣言方法を制御することにより、変数をオブジェクトにラップすることを選択できるので、基本的に常に参照で渡すことができます。これは上記のEduardo Cuomoの回答の1つに似ていますが、以下の解決策では、変数識別子として文字列を使用する必要はありません。概念を示すための最小限のコードを次に示します。

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インスタンスでのみ機能します。これは動作しません。
エドゥアルドクオモ2018年

2

個人的には、さまざまなプログラミング言語が提供する「参照渡し」機能が嫌いです。たぶん、関数型プログラミングの概念を発見しているだけなのかもしれませんが、副作用を引き起こす関数(参照によって渡されるパラメーターの操作など)を見ると、常に鳥肌が立ちます。私は個人的に「単一責任」の原則を強く受け入れています。

IMHO、関数はreturnキーワードを使用して1つの結果/値のみを返す必要があります。パラメーター/引数を変更する代わりに、変更したパラメーター/引数の値を返し、必要な再割り当てを呼び出しコードに任せます。

しかし、時には(できれば非常にまれに)、同じ関数から2つ以上の結果値を返す必要がある場合もあります。その場合、それらの結果の値をすべて単一の構造またはオブジェクトに含めることを選択します。この場合も、再割り当ての処理は呼び出し元のコードで行う必要があります。

例:

引数リストで '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()がそのことを行うことを期待しています。そのことを行うのではなく、オブジェクトを作成して、変数をプロパティに割り当てます。検索する必要のある配列があるとします。一致する項目を2番目の配列に配置し、見つかった数を知りたいとしましょう。標準的な解決策は、次のような関数を呼び出すことです: 'var foundArray; if((findStuff(inArray、&foundArray))> 1){//プロセスfoundArray} '。そのシナリオのどこにも、新しい未知のオブジェクトが望ましいまたは必要な場所はありません。
Elise van Looij

@ElisevanLooijあなたの例の場合、私はfindStuff単に結果の配列を返すことを好むでしょう。foundArray変数も宣言する必要があるので、結果の配列を変数に直接割り当てますvar foundArray = findStuff(inArray); if (foundArray.length > 0) { /* process foundArray */ }。これにより、1)呼び出しコードがより読みやすく/理解しやすくなり、2)findStuffメソッドの内部機能(したがってテスト容易性)が大幅に簡素化され、実際にはさまざまな(再)ユースケース/シナリオでの柔軟性が大幅に向上します。
Bart Hofland

@ElisevanLooijしかし、私は(私の回答のように)再割り当てが実際に「コードのにおい」であることに同意します。実際には、それらもできるだけ避けたいと思います。私は自分の答えを編集する(あるいは再定式化する)ことについて考えます。それは、主題に関する実際の意見をよりよく反映するような方法です。あなたの反応をありがとう。:)
バートホフランド

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は強力なタイプではないため、このトレッドにあるように、さまざまな方法で問題を解決できます。

ただし、保守性の観点からは、バートホフランドに同意する必要があります。関数は引数を取得して何かを実行し、結果を返す必要があります。簡単に再利用できるようにします。

変数を参照渡しする必要があると思われる場合は、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.