448

JavaScript は参照渡しですか、それとも値渡しですか?

以下はJavaScript の例です: The Good Parts . my長方形関数のパラメーターについて非常に混乱しています。実際にはundefinedであり、関数内で再定義されています。元の参照はありません。関数パラメーターから削除すると、内側の領域関数はそれにアクセスできなくなります。

閉鎖ですか?しかし、関数は返されません。

var shape = function (config) {
    var that = {};
    that.name = config.name || "";
    that.area = function () {
        return 0;
    };
    return that;
};

var rectangle = function (config, my) {
    my = my || {};
    my.l = config.length || 1;
    my.w = config.width || 1;
    var that = shape(config);
    that.area = function () {
        return my.l * my.w;
    };
    return that;
};

myShape = shape({
    name: "Unhnown"
});

myRec = rectangle({
    name: "Rectangle",
    length: 4,
    width: 6
});

console.log(myShape.name + " area is " + myShape.area() + " " + myRec.name + " area is " + myRec.area());
4

13 に答える 13

816

プリミティブは値で渡され、オブジェクトは「参照のコピー」で渡されます。

具体的には、オブジェクト (または配列) を渡す場合、(目に見えない形で) そのオブジェクトへの参照を渡していることになり、そのオブジェクトの内容を変更することは可能ですが、参照を上書きしようとしても、コピーには影響しません。呼び出し元が保持する参照 - つまり、参照自体は値によって渡されます。

function replace(ref) {
    ref = {};           // this code does _not_ affect the object passed
}

function update(ref) {
    ref.key = 'newvalue';  // this code _does_ affect the _contents_ of the object
}

var a = { key: 'value' };
replace(a);  // a still has its original value - it's unmodfied
update(a);   // the _contents_ of 'a' are changed
于 2012-10-27T21:51:24.643 に答える
71

次のように考えてください。

ECMAscript でオブジェクトを作成するときはいつでも、このオブジェクトは、誰も到達できない神秘的な ECMAscript の普遍的な場所で形成されます。返されるのは、この神秘的な場所にあるそのオブジェクトへの参照だけです。

var obj = { };

Evenobjはオブジェクト (その特別な素晴らしい場所にある) への参照にすぎないため、この参照のみを渡すことができます。事実上、 objにアクセスするコードは、遠く離れたオブジェクトを変更します。

于 2012-10-27T21:55:52.213 に答える
61

私の 2 セント.... JavaScript がパラメーターを参照または値で渡すかどうかは関係ありません。本当に重要なのは、代入と突然変異です。

このリンクに、より長く、より詳細な説明を書きました。

何か (オブジェクトであろうとプリミティブであろうと) を渡すと、JavaScript は関数内で新しい変数を割り当てるだけです... 等号 ( =) を使用するのと同じように。

そのパラメーターが関数内でどのように動作するかは、等号を使用して新しい変数を割り当てた場合の動作とまったく同じです... これらの簡単な例を見てください。

var myString = 'Test string 1';

// Assignment - A link to the same place as myString
var sameString = myString;

// If I change sameString, it will not modify myString,
// it just re-assigns it to a whole new string
sameString = 'New string';

console.log(myString); // Logs 'Test string 1';
console.log(sameString); // Logs 'New string';

myStringパラメータとして関数に渡すと、関数は単に新しい変数に代入したかのように動作します。さて、同じことをしましょう。ただし、単純な割り当ての代わりに関数を使用します。

function myFunc(sameString) {

  // Reassignment... Again, it will not modify myString
  sameString = 'New string';
}

var myString = 'Test string 1';

// This behaves the same as if we said sameString = myString
myFunc(myString);

console.log(myString); // Again, logs 'Test string 1';

オブジェクトを関数に渡すときにオブジェクトを変更できる唯一の理由は、再割り当てを行っていないためです...代わりに、オブジェクトを変更または変更できます....繰り返しますが、同じように機能します。

var myObject = { name: 'Joe'; }

// Assignment - We simply link to the same object
var sameObject = myObject;

// This time, we can mutate it. So a change to myObject affects sameObject and visa versa
myObject.name = 'Jack';
console.log(sameObject.name); // Logs 'Jack'

sameObject.name = 'Jill';
console.log(myObject.name); // Logs 'Jill'

// If we re-assign it, the link is lost
sameObject = { name: 'Howard' };
console.log(myObject.name); // Logs 'Jill'

myObjectパラメータとして関数に渡すと、関数は単に新しい変数に代入したかのように動作します。繰り返しますが、まったく同じ動作で同じことですが、関数があります。

function myFunc(sameObject) {
  // We mutate the object, so the myObject gets the change too... just like before.
  sameObject.name = 'Jill';

  // But, if we re-assign it, the link is lost
  sameObject = {
    name: 'Howard'
  };
}

var myObject = {
  name: 'Joe'
};

// This behaves the same as if we said sameObject = myObject;
myFunc(myObject);
console.log(myObject.name); // Logs 'Jill'

変数を関数に渡すたびに、等号を使用した場合と同様に、パラメーターの名前が何であれ「代入」しています=

等号=は割り当てを意味することを常に覚えておいてください。また、関数にパラメーターを渡すことは、代入も意味します。それらは同じであり、2 つの変数はまったく同じ方法で接続されます。

変数の変更が別の変数に影響を与えるのは、基になるオブジェクトが変更されたときだけです。

オブジェクトとプリミティブを区別しても意味がありません。関数がなく、等号を使用して新しい変数に代入した場合とまったく同じように機能するからです。

于 2014-08-07T07:49:40.603 に答える
33

関数の引数は、値渡しまたは共有渡しのいずれかで渡されますが、JavaScript では決して参照渡しではありません!

値渡し

プリミティブ型は値渡しされます。

var num = 123, str = "foo";

function f(num, str) {
  num += 1;
  str += "bar";
  console.log("inside of f:", num, str);
}

f(num, str);
console.log("outside of f:", num, str);

関数スコープ内の再割り当ては、周囲のスコープには表示されません。

Stringこれは、複合データ型でありながら不変である s にも当てはまります。

var str = "foo";

function f(str) {
  str[0] = "b"; // doesn't work, because strings are immutable
  console.log("inside of f:", str);
}

f(str);
console.log("outside of f:", str);

コールバイシェアリング

オブジェクト、つまりプリミティブでないすべての型は、共有によって渡されます。オブジェクトへの参照を保持する変数は、実際にはこの参照のコピーを保持するだけです。JavaScript が参照による呼び出しの評価戦略を追求する場合、変数は元の参照を保持します。これが、共有と参照の決定的な違いです。

この区別の実際的な結果は何ですか?

var o = {x: "foo"}, p = {y: 123};

function f(o, p) {
  o.x = "bar"; // Mutation
  p = {x: 456}; // Reassignment
  console.log("o inside of f:", o);
  console.log("p inside of f:", p);
}

f(o, p);

console.log("o outside of f:", o);
console.log("p outside of f:", p);

ミューティングとは、既存の の特定のプロパティを変更することを意味しObjectます。変数がバインドされ、このオブジェクトを参照する参照コピーは変わりません。したがって、ミューテーションは呼び出し元のスコープで表示されます。

再割り当てとは、変数にバインドされた参照コピーを置き換えることを意味します。これは単なるコピーであるため、同じ参照のコピーを保持する他の変数は影響を受けません。したがって、再割り当ては、参照渡しの評価戦略の場合のように、呼び出し元のスコープでは表示されません。

ECMAScriptの評価戦略に関する詳細情報。

于 2016-07-22T19:05:25.660 に答える
22

C と同様に、最終的にはすべてが値渡しされます。C とは異なり、実際には変数の場所をバックアップして渡すことはできません。変数にはポインターがなく、参照だけであるためです。

そしてそれが持っている参照はすべて、変数ではなくオブジェクトへのものです。同じ結果を得るにはいくつかの方法がありますが、呼び出しまたは宣言のサイトにキーワードを追加するだけでなく、手作業で行う必要があります。

于 2012-10-28T01:43:21.067 に答える
9

JavaScript は値渡しです。

プリミティブの場合、プリミティブの値が渡されます。オブジェクトの場合、オブジェクトの参照「値」が渡されます。

オブジェクトの例:

var f1 = function(inputObject){
    inputObject.a = 2;
}

var f2 = function(){
    var inputObject = {"a": 1};
    f1(inputObject);
    console.log(inputObject.a);
}

f2 を呼び出すと、参照が渡され、参照の "a" 値が更新されるため、"a" 値が 1 ではなく 2 として出力されます。

プリミティブの例:

var f1 = function(a){
    a = 2;
}
var f2 = function(){
    var a = 1;
    f1(a);
    console.log(a);
}

f2 を呼び出すと、「a」の値が 1 として出力されます。

于 2014-03-08T08:48:30.900 に答える
4

実用的にはAlnitakが正解でわかりやすいですが、最終的にJavaScriptではすべて値渡しです。

物の「価値」とは?オブジェクト参照です。

オブジェクトを渡すと、この値のコピーが取得されます (したがって、Alnitak が説明した「参照のコピー」)。この値を変更しても、元のオブジェクトは変更されません。その参照のコピーを変更しています。

于 2013-11-22T19:39:28.250 に答える
3

「グローバル」JavaScript 変数は、window オブジェクトのメンバーです。window オブジェクトのメンバーとして参照にアクセスできます。

var v = "initialized";

function byref(ref) {
  window[ref] = "changed by ref";
}

byref((function(){for(r in window){if(window[r]===v){return(r);}}})());
// It could also be called like... byref('v');
console.log(v); // outputs changed by ref

上記の例は、関数内で宣言された変数に対しては機能しないことに注意してください。

于 2014-07-24T21:49:18.057 に答える
2

純粋主義がなければ、JavaScript で参照によってスカラー引数をエミュレートする最良の方法は、以前の回答が示すように、オブジェクトを使用することだと思います。

しかし、私は少し違うことをします:

関数呼び出し内でオブジェクトの割り当てを行ったので、関数呼び出しの近くに参照パラメーターが表示されます。ソースの可読性が向上します。

関数宣言では、読みやすさというまったく同じ理由で、プロパティをコメントのように置きます。

var r;

funcWithRefScalars(r = {amount:200, message:null} );
console.log(r.amount + " - " + r.message);


function funcWithRefScalars(o) {  // o(amount, message)
  o.amount  *= 1.2;
  o.message = "20% increase";
}

上記の例では、nullは出力参照パラメータを明確に示しています。

出口:

240 - 20% Increase

クライアント側では、console.logに置き換える必要がありますalert

★ ★ ★</p>

さらに読みやすい別の方法:

var amount, message;

funcWithRefScalars(amount = [200], message = [null] );
console.log(amount[0] + " - " + message[0]);

function funcWithRefScalars(amount, message) {  // o(amount, message)
   amount[0]  *= 1.2;
   message[0] = "20% increase";
}

ここでは、上記のように新しいダミー名を作成する必要さえありませんr

于 2018-03-09T17:46:53.403 に答える