プリミティブ型(Number、Stringなど)は値渡しされますが、オブジェクトは不明です。オブジェクトは両方とも値渡し可能です(オブジェクトを保持する変数が実際にはオブジェクトへの参照であると考える場合)。 (オブジェクトへの変数がオブジェクト自身を保持していると考えるとき).
最後にはそれほど重要ではありませんが、規則を渡す引数を提示するための正しい方法は何かを知りたいです。 JavaScriptの仕様からの抜粋はありますか。
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.item
を変更しても関数外のobj1
には影響しません。num
は100
になり、obj2.item
は"changed"
になります。代わりに、渡された項目が値によって渡されるという状況があります。しかし、値によって渡される項目は自身参照です。技術的には、これは call-by-sharing と呼ばれます。
実際的には、これは(num
やobj2
のように)パラメータ自体を変更しても、パラメータに渡された項目には影響しないことを意味します。しかし、パラメータの _ internals _ を変更すると、(obj1
と同様に)元に戻ります。
常に値渡しですが、オブジェクトにとっては変数の値は参照です。このため、オブジェクトを渡してそのオブジェクトの members を変更しても、それらの変更は関数の外部で持続します。これにより、 look のように参照渡しになります。しかし、実際にオブジェクト変数の値を変更した場合、その変更は持続しないことがわかり、それが本当に値渡しであることを証明します。
例:
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
変数はオブジェクトを「保持」せず、参照を保持します。その参照を別の変数に割り当てることができます。両方とも同じオブジェクトを参照します。常に値で渡されます(その値が参照である場合でも)。
パラメータとして渡された変数が保持する値を変更する方法はありません。これは、JSが参照渡しをサポートしている場合に可能になります。
私の2セント……これが私の理解の仕方です。 (私が間違っている場合は私を修正してください)
値/参照によるパスについて知っているすべてのものを捨てる時が来ました。
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を変更できませんでした。文字列は不変であり、変更していないためです。新しい文字列への "RE ASSIGN"のfavoriteStringだけです。これは本質的にそれをstring1から切り離しました。前の例では、オブジェクトの名前を変更したときに、何も割り当てませんでした。その代わりに、2つの変数と基礎となるオブジェクトとの間の接続を維持するオブジェクトを単純に変更しました。
関数を呼び出してパラメータを渡すと、基本的に新しい変数への「代入」が行われます。これは、単純にを使用して割り当てた場合とまったく同じように機能します。等号(=).
これらの例を見てください。
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
otherObj = {
firstName: 'Jack',
lastName: 'Frost'
};
// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object no longer mutates 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 no longer mutates 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で関数呼び出しがどのように機能するのかをよく理解できたはずです。参照渡しでも値渡しでもかまいません。問題は代入と突然変異です。
変数を関数に渡すたびに、等号(=)を使用した場合と同じように、パラメータ変数の名前が何であっても代入します。
等号(=)は代入を意味することを常に覚えておいてください。関数にパラメータを渡すことも代入を意味することを常に覚えておいてください。それらは同じで、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'
次の点を考慮してください。
そう、 "参照/値渡し" を忘れてください 「参照/値渡し」でハングアップしないでください。
あなたの質問に答えるには:ポインタが渡されます。
// 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
関数の外側のオブジェクトは、外側のオブジェクトへの参照を与えることによって関数に渡されます。その参照を使用してそのオブジェクトを操作すると、外側のオブジェクトが影響を受けます。しかし、関数の内部で他のものへの参照を指すことにした場合は、外部のオブジェクトにはまったく影響を与えませんでした。
これを次のように考えてください。それは常に値渡しです。ただし、オブジェクトの値はオブジェクト自体ではなく、そのオブジェクトへの参照です。
これは数値を渡す例です(プリミティブ型)
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}
Javascriptは常に 値渡し です。すべて値型です。オブジェクトは値であり、オブジェクトのメンバー関数は値そのものです(関数はJavascriptのファーストクラスオブジェクトです)。また、Javascriptのすべてが object であるという概念に関しては、これは間違っています。文字列、シンボル、数字、ブール値、null、未定義は プリミティブ です。場合によっては、基本プロトタイプから継承したメンバー関数やプロパティを利用することができますが、これは単に便宜上のものであり、それらがオブジェクト自体であるという意味ではありません。以下を参考にしてください
x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);
どちらのアラートでも、値が未定義であることがわかります。
JavaScriptでは、値の型{only}によって、その値を 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
はその値の初期コピーを1つ保持し、b
はその値の別のコピーに割り当てられます。 b
を変更するとき、a
の値を変更することは決してありません。
しかし、c
とd
はどちらも、同じ共有値[1,2,3]
への別々の参照です。これは複合値です。 c
もd
も[1,2,3]
の値を「所有」するものではないことに注意することが重要です。どちらも値に対する同等のピア参照です。そのため、どちらかの参照を使用して実際の共有array
値自体を変更(.Push(4)
)した場合、それは1つの共有値のみに影響し、両方の参照は新しく変更された値[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
をa
への参照ではなくarray
へのポインタにする必要があります。ただし、そのような機能はJSにはありません。
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
に割り当てられます。 x
とa
は、同じ[1,2,3]
値を指す別々の参照です。これで、関数内で、その参照を使用して値自体を変更できます(Push(4)
)。しかし、x = [4,5,6]
を代入するとき、これは初期参照a
が指している場所にはまったく影響しません - それでも(今修正された)[1,2,3,4]
値を指しています。
Value-copyによって複合値(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
を見ます。
値による、参照によるコピー、受け渡し、比較についての非常に詳細な説明は この章 "JavaScript:The Definitive Guide"の本にあります。
参照によってオブジェクトと配列を操作するというトピックを離れる前に、命名法のポイントを明確にする必要があります。 「参照渡し」という句にはいくつかの意味があります。一部の読者にとって、この句は、関数が新しい値をその引数に割り当て、それらの変更された値を関数の外から見えるようにすることを可能にする関数呼び出し手法を指します。これはこの本で使われている用語ではありません。ここでは、単にオブジェクトそのものではなく、オブジェクトまたは配列への参照が関数に渡されることを意味します。関数は参照を使用して、オブジェクトのプロパティまたは配列の要素を変更できます。しかし、関数がその参照を新しいオブジェクトまたは配列への参照で上書きした場合、その変更は関数の外からは見えません。この用語の他の意味に精通している読者は、オブジェクトと配列が値によって渡されると言うことを好むかもしれませんが、渡される値は実際にはオブジェクト自体よりもむしろ参照です。
1つ私がまだ理解することができないこと。下記のコードを確認してください。何かご意見は?
function A() {}
A.prototype.foo = function() {
return 'initial value';
}
function B() {}
B.prototype.bar = A.prototype.foo;
console.log(A.prototype.foo()); //initial value
console.log(B.prototype.bar()); //initial value
A.prototype.foo = function() {
return 'changed now';
}
console.log(A.prototype.foo()); //changed now
console.log(B.prototype.bar()); //Why still 'initial value'???
さて、人々は、「参照渡し」がJavaなどを説明する正しい方法であるかどうかについて際限なくぐずぐずしています。実際に行います。ポイントはこれです:
- オブジェクトを渡しても、オブジェクトはコピーされません。
- 関数に渡されたオブジェクトは、そのメンバーが関数によって変更されることがあります。
- 関数に渡された基本値は、その関数では変更できません。コピーが作成されます。
私の本ではそれを参照渡しと呼んでいます。
- Brian Bi - どのプログラミング言語が参照渡しか
これは反論です。
セマンティクス!!具体的な定義を設定すると、同じ単語やフレーズを使用しても同じことを記述していないため、必ずいくつかの回答とコメントに互換性がなくなりますが、混乱を克服することが重要です(特に新しいプログラマーにとって)。
まず第一に、誰もが把握できるとは限らない抽象化の複数のレベルがあります。第4世代または第5世代の言語で学んだ新しいプログラマーは、ポインターへのポインターへのポインターによって段階化されていないアセンブリまたはCプログラマーに馴染みのある概念に心を包むのが難しいかもしれません。参照渡しとは、単に関数パラメーター変数を使用して参照先オブジェクトを変更する機能を意味するものではありません。
変数:メモリ内の特定の位置にある値を参照するシンボルの複合概念。通常、この用語は、詳細を議論する際に単独で使用するにはあまりにも負荷が大きい。
シンボル:変数を参照するために使用されるテキスト文字列(つまり、変数の名前)。
値:メモリに保存され、変数のシンボルを使用して参照される特定のビット。
メモリの場所:変数の値が保存される場所。 (場所自体は、その場所に保存されている値とは別の数字で表されます。)
関数パラメーター:関数定義で宣言された変数。関数に渡された変数の参照に使用されます。
関数の引数:呼び出し元によって関数に渡される関数外の変数。
オブジェクト変数:基本的な基礎値が「オブジェクト」そのものではなく、その値がオブジェクトの実際のデータが保存されているメモリ内の別の場所へのポインタ(メモリ位置値)である変数。ほとんどの高世代言語では、「ポインター」の側面は、さまざまなコンテキストでの自動逆参照によって効果的に隠されます。
プリミティブ変数:値ISが実際の値である変数。この概念は、さまざまな言語のオートボクシングやオブジェクトのようなコンテキストによって複雑になる場合がありますが、一般的な考え方は、変数の値ISは、別のメモリへのポインタではなく変数のシンボルによって表される実際の値ですロケーション。
関数の引数とパラメーターは同じものではありません。また、変数の値は変数のオブジェクトではありません(さまざまな人が既に指摘しているようですが、明らかに無視されます)。これらの区別は、適切な理解に不可欠です。
値渡しまたは共有呼び出し(オブジェクトの場合):関数の引数の値は、関数のパラメーターシンボルによって参照される別のメモリ位置にコピーされます(スタック上にあるかどうかに関係なく)ヒープ)。言い換えると、関数パラメーターは、渡された引数の値のコピーを受け取りました...((重要)引数の値IS NEVER UPDATED/ALTERED/CHANGED by calling function)。オブジェクト変数の値はオブジェクトそのものではなく、オブジェクトへのポインターであるため、値によってオブジェクト変数を渡すと、関数パラメーター変数へのポインターがコピーされることに注意してください。関数パラメーターの値は、メモリ内のまったく同じオブジェクトを指します。オブジェクトデータ自体は、関数パラメーターを介して直接変更できますが、関数の引数の値はIS NEVER UPDATEDなので、same関数呼び出し全体およびその後のオブジェクト(オブジェクトのデータが変更された場合、または関数パラメーターに異なるオブジェクトがすべて割り当てられた場合でも)。参照されるオブジェクトが関数パラメーター変数を介して更新可能であるという理由だけで、関数の引数が参照によって渡されたと結論付けるのは正しくありません。
Call/Pass-by-reference:関数の引数の値は、対応する関数パラメーターによって直接更新できます/更新されます。それが役立つ場合、関数パラメータは引数の有効な「エイリアス」になります。同じメモリ位置にある同じ値を効果的に参照します。関数の引数がオブジェクト変数である場合、関数のパラメーターは引数と同じオブジェクトを指すため、オブジェクトのデータを変更する機能は値渡しの場合と変わりません。しかし、オブジェクト変数の場合、関数パラメーターが完全に異なるオブジェクトに設定されている場合、引数も同様に異なるオブジェクトを指します。これは、値渡しの場合には発生しません。
JavaScriptは参照渡しされません。よく読んでみると、反対意見はすべて、値渡しの意味を誤解しており、関数パラメーターを介してオブジェクトのデータを更新する機能は「値渡し」と同義であると誤解していることに気付くでしょう。
Object clone/copy:新しいオブジェクトが作成され、元のオブジェクトのデータがコピーされます。これはディープコピーでもシャローコピーでもかまいませんが、ポイントは新しいオブジェクトが作成されることです。オブジェクトのコピーの作成は、値渡しとは別の概念です。一部の言語では、クラスオブジェクトと構造体(など)を区別し、異なる型の変数を渡すために異なる動作をする場合があります。しかし、JavaScriptはオブジェクト変数を渡すときにこのようなことを自動的に行いません。ただし、オブジェクトの自動複製が存在しないからといって、参照渡しに変換されるわけではありません。
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;
}
これは値渡しと参照渡し(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
参照渡し(オブジェクト)
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" }
特別な場合、参照渡し(オブジェクト)
c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
私はこれらの答えを何度も読みましたが、Barbara Liskovと呼ばれる "Call by sharing" の技術的定義について知るまでは、本当にそれを得ませんでした
共有による呼び出しのセマンティクスは、関数内の関数引数への割り当てが呼び出し元から見えないという点で参照による呼び出しとは異なります[参照の必要性]。変数が渡された場合、呼び出し側のスコープ内でその変数への代入をシミュレートすることはできません。ただし、関数は呼び出し元と同じオブジェクトにアクセスできるため(コピーは作成されません)、オブジェクト内で変更可能な場合、関数内でそれらのオブジェクトへの変更は呼び出し側から認識されます。意味論オブジェクトはコピーもクローンもされていないため、関数内の変更可能なオブジェクトの変更は呼び出し元に表示され、共有されます。
つまり、パラメータ値自体にアクセスしてアクセスすれば、パラメータ参照は変更可能です。一方、パラメータへの割り当ては評価後に消え、関数呼び出し元からはアクセスできなくなります。
これを理解するための私の簡単な方法は...
関数を呼び出すときは、変数自体ではなく、引数変数の内容(参照または値)を渡しています。
var var1 = 13;
var var2 = { prop: 2 };
//13 and var2's content (reference) are being passed here
foo(var1, var2);
関数内では、パラメータ変数inVar1
とinVar2
が、渡される内容を受け取ります。
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;
}
プログラミング言語の弁護士については、ECMAScript 5.1の次のセクション(最新版より読みやすい)を調べ、ECMAScriptメーリングリストで asking にまで進みました。
TL; DR:値はすべて渡されますが、オブジェクトのプロパティは参照であり、標準ではオブジェクトの定義が不気味に欠けています。
11.2.4項「引数リスト」には、1つの引数のみで構成される引数リストの作成に関する次のことが記載されています。
プロダクションArgumentList:AssignmentExpressionは、次のように評価されます。
- RefをAssignmentExpressionの評価結果とします。
- ArgをGetValue(ref)とします。
- 唯一の項目がargであるリストを返します。
このセクションでは、引数リストに0または> 1の引数があるケースも列挙します。
したがって、すべてのものは参照によって渡されます。
11.2.1項「プロパティ・アクセッサ」
プロダクションMemberExpression:MemberExpression [Expression]は、次のように評価されます。
- BaseReferenceをMemberExpressionの評価結果とします。
- BaseValueをGetValue(baseReference)とします。
- PropertyNameReferenceをExpressionの評価結果とします。
- PropertyNameValueをGetValue(propertyNameReference)とします。
- CheckObjectCoercible(baseValue)を呼び出します。
- PropertyNameStringをToString(propertyNameValue)とします。
- 評価されている構文生成物がストリクトモードコードに含まれている場合は、strictをtrueにし、strictをfalseにします。
- 参照型の値を返します ベース値がbaseValueであり、参照名がpropertyNameStringであり、ストリクトモードフラグがstrictであるもの。
したがって、オブジェクトのプロパティは常に参照として使用できます。
セクション8.7「参照仕様タイプ」で説明されているように、参照は言語の実際のタイプではなく、削除、typeof、および代入演算子の動作を記述するためにのみ使用されます。
5.1エディションでは、「オブジェクトはプロパティのコレクション」と定義されています。したがって、オブジェクトの値はコレクションであると推測できますが、コレクションの値については仕様で不十分に定義されており、理解するには少しの effort が必要です。
低レベル言語では、参照によって変数を渡したい場合は、関数の作成に特定の構文を使用する必要があります。
int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
*age = *age + 1;
}
&age
はmyAge
への参照ですが、値が必要な場合は*age
を使用して参照を変換する必要があります。
Javascriptはあなたに代わってこの変換を行う高級言語です。そのため、オブジェクトは参照によって渡されますが、言語は参照パラメータを値に変換します。関数定義で&
を使用して参照で渡す必要はありません。参照を値に変換するために関数本体で*
を使用する必要はありません。JSはそれを行います。
そのため、値を置き換えることによって関数内のオブジェクトを変更しようとすると(つまりage = {value:5}
)、変更は持続しませんが、プロパティを変更すると(つまりage.value = 5
)、変更されます。
javaScriptでの参照について私が知っていることを共有する
Javascriptでは、 objects は参照として格納されています。
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
私が見つけた最も簡潔な説明は 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
すなわち基本的にプリミティブ型は値渡しされ、複合型は参照渡しされます。
何かが「参照渡し」かどうかを判断する簡単な方法は、「スワップ」関数を書くことができるかどうかです。たとえば、Cでは、次のことができます。
void swap(int *i, int *j)
{
int t;
t = *i;
*i = *j;
*j = t;
}
Javascriptでそれと同等のことができない場合、それは「参照渡し」ではありません。
Underscore.jsライブラリ の extend method は、オブジェクトをパラメータとして渡したい場合に非常に便利であることがわかりました。
function replaceOrModify(aObj) {
if (modify) {
aObj.setNewValue('foo');
} else {
var newObj = new MyObject();
// _.extend(destination, *sources)
_.extend(newObj, aObj);
}
}
MDNのドキュメントでは、冗長すぎることなく、明確に説明されています。
関数呼び出しのパラメータは、関数のargumentsです。引数は関数valueによって渡されます。関数が引数の値を変更した場合、この変更はグローバルにも呼び出し元の関数にも反映されません。オブジェクト参照も値であり、それらは特別なものです。関数が参照されるオブジェクトのプロパティを変更した場合、その変更は関数の外側に表示されます。(...)
ソース: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description
私はそれがコピーによるパスであると言うでしょう -
引数と変数オブジェクトは、関数呼び出しの始めに作成された実行コンテキストの間に作成されたオブジェクトであり、関数に渡された実際の値/参照はこの引数+変数オブジェクトに格納されます。
簡単に言えば、プリミティブ型の場合、値は関数呼び出しの始めにコピーされ、オブジェクト型の場合、参照はコピーされます。