JSで変数を参照渡しする方法は?私はいくつかの操作を実行したいので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
これを行うための最良の方法は何ですか?
JavaScriptで利用できる「参照渡し」はありません。オブジェクトを渡すことができ(つまり、値への参照をオブジェクトへの参照に渡すことができます)、関数にオブジェクトの内容を変更させることができます。
function alterObject(obj) {
obj.foo = "goodbye";
}
var myObj = { foo: "hello world" };
alterObject(myObj);
alert(myObj.foo); // "goodbye" instead of "hello world"
さて、あなたの場合、とにかく何も渡していません。必要に応じて、数値インデックスを使用して配列のプロパティを反復処理し、配列の各セルを変更できます。
「参照渡し」は非常に具体的な用語であることに注意することが重要です。変更可能なオブジェクトへの参照を渡すことができるというだけではありません。代わりに、関数がcallingコンテキストでその値を変更できるように単純な変数を渡すことができることを意味します。そう:
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++のような言語では、その言語does(sort-of)に参照渡しがあるため、これを行うことができます。
edit —これは最近(2015年3月)、以下の私のブログに似たブログ投稿でRedditで再び爆発しましたが、この場合はJavaについてです。 Redditのコメントの前後を読みながら、混乱の大部分は「参照」という言葉が関係する不幸な衝突に起因していると思いました。 「参照による受け渡し」および「値による受け渡し」という用語は、プログラミング言語で使用する「オブジェクト」を持つという概念よりも前のことです。本当にオブジェクトに関するものではありません。これは、関数パラメーター、具体的には関数パラメーターが呼び出し環境に「接続」される(または接続されない)方法に関するものです。具体的には、真の参照渡し言語-doesがオブジェクトを含む言語-では、オブジェクトを変更する機能をまだ持っていることに注意してくださいcontents JavaScriptの場合とほぼ同じです。ただし、呼び出し環境でオブジェクト参照を変更できるのはalsoであり、これがJavaScriptでca n'tすることが重要なことです。参照渡し言語は、参照そのものではなく、参照への参照を渡します。
編集 — トピックに関するブログ投稿があります。 (C++には実際に参照渡しがないことを説明するその投稿へのコメントに注意してください。それは本当です。ただし、C++には、関数呼び出しの時点で明示的にポインターを作成するか、または引数タイプシグネチャがそれを呼び出す関数を呼び出すときにimplicitlyのいずれかで、プレーン変数への参照を作成する機能があります。 JavaScriptがサポートしていない重要なものです。)
配列とオブジェクトは、参照によって、またはこれらの条件に基づく値によって渡されます。
オブジェクトまたは配列の値を設定している場合は、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
参照のように変数を渡すための回避策:
var a = 1;
inc = function(variableName) {
window[variableName] += 1;
};
inc('a');
alert(a); // 2
_ edit _
うん、実際にあなたはグローバルアクセスなしでそれを行うことができます
inc = (function () {
var variableName = 0;
var init = function () {
variableName += 1;
alert(variableName);
}
return init;
})();
inc();
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
参照によって(ローカル、プリミティブ)変数を渡すためのさらに別のアプローチは、eval
で「オンザフライ」でクロージャ付きの変数をラップすることです。これは "use strict"でも機能します。 (注:eval
はJSオプティマイザーにとって使いやすいものではありません。また、変数名の前後に引用符を付けないと、予期しない結果が生じる可能性があります。)
"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/
実際にはかなりの解決策があります。
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"]
私はこの種のことをするために構文で遊んできましたが、それは少し珍しいいくつかのヘルパーを必要とします。最初は '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);
});
問題は、古典的な引数を渡すと、別の読み取り専用ゾーンにスコープされることを理解していることです。
グローバル/スコープ変数に引数を入れるのと同じですが、より良い...
function action(){
/* process this.arg, modification allowed */
}
action.arg = [ ["empty-array"],"some string",0x100,"last argument" ];
action();
また、promiseよく知られたチェーンを楽しむことができます:ここに約束のような構造
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()
私は個人的には、さまざまなプログラミング言語が提供する「参照渡し」機能が嫌いです。たぶんそれは私が関数型プログラミングの概念を発見しているだけなのですが、副作用を引き起こす関数(参照渡しされたパラメータの操作のような)を見るときいつも私はグースバンプを得る。私は個人的に「単一責任」の原則を強く受け入れます。
私見、関数はreturnキーワードを使ってたった一つの結果/値を返すべきです。パラメータ/引数を変更する代わりに、変更したパラメータ/引数の値を返し、必要な再割り当てを呼び出し元のコードに任せるだけです。
しかし時には(うまくいけばごくまれに)、同じ関数から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);
これらのコード例は非常に単純化されていますが、私が個人的にそのようなものを処理する方法を大まかに示しています。それは私が様々な責任を正しい場所に保つのに役立ちます。
ハッピーコーディング:)
参照渡しの議論を別にすれば、まだ述べられた質問の解決策を探している人は以下を使用できます。
const myArray = new Array(var1, var2, var3);
myArray.forEach(var => var = makePretty(var));
私はあなたの言っていることを正確に知っています。 Swiftでも同じことは問題になりません。要点はlet
ではなくvar
を使うことです。
プリミティブは値渡しされるが、繰り返しの時点でのvar i
の値が無名関数にコピーされないという事実は、控えめに言っても驚くべきことです。
for (let i = 0; i < boxArray.length; i++) {
boxArray[i].onclick = function() { console.log(i) }; // correctly prints the index
}
JSは強力な型ではないため、このトレッドに見られるように、さまざまな方法で問題を解決できます。
しかし、保守性の観点からは、Bart Hoflandに同意する必要があります。関数は、何かをして結果を返すように引数を取得する必要があります。それらを簡単に再利用可能にします。
変数を参照渡しする必要があると思われる場合は、それらをオブジェクトIMHOに組み込むことをお勧めします。
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]