92

私が理解しているこのコード。Aのコピーを作成し、それをCと呼びます。Aが変更されても、Cは同じままです。

var A = 1;
var C = A;
console.log(C); // 1
A++;
console.log(C); // 1

しかし、Aが配列の場合、異なる位置付けになります。Cが変わるだけでなく、Aに触れる前に変わる

var A = [2, 1];
var C = A;
console.log(C); // [1, 2]
A.sort();
console.log(C); // [1, 2]

誰かが2番目の例で何が起こったのか説明できますか?

4

7 に答える 7

84

Console.log()オブジェクトへの参照が渡されるため、オブジェクトの変更に応じてコンソールの値が変更されます。それを回避するには、次のことができます。

console.log(JSON.parse(JSON.stringify(c)))

MDNは警告します:

ChromeとFirefoxの最新バージョンでオブジェクトをログに記録する場合、コンソールに記録されるのはオブジェクトへの参照であり、呼び出した時点でのオブジェクトの「値」である必要はありませんconsole.log()が、これは、コンソールを開いたときのオブジェクトの値です。

于 2018-02-25T02:21:56.800 に答える
47

Pointyの答えには良い情報がありますが、この質問に対する正しい答えではありません。

OPによって記述された動作は、2010年3月に最初に報告され、2012年8月にWebkitにパッチが適用されたバグの一部ですが、この記事の執筆時点ではまだGoogleChromeに統合されていません。動作は、オブジェクトリテラルがに渡されたときにコンソールデバッグウィンドウが開いている閉じconsole.log()ているかによって異なります。

元のバグレポート( https://bugs.webkit.org/show_bug.cgi?id=35801 )からの抜粋:

説明ミッチクラマーから2010-03-0511:37:45PST

1)1つ以上のプロパティを持つオブジェクトリテラルを作成します

2)そのオブジェクトをconsole.logしますが、閉じたままにします(コンソールで展開しないでください)

3)プロパティの1つを新しい値に変更します

ここでそのconsole.logを開くと、生成された時点で値が異なっていたとしても、何らかの理由で新しい値が設定されていることがわかります。

開いた場合、それが明確でない場合は正しい値を保持することを指摘しておく必要があります。

Chromium開発者からの応答:

コメント#2 Pavel Feldman 2010-03-0906:33:36PSTから

これを修正するつもりはないと思います。オブジェクトをコンソールにダンプするときにクローンを作成することはできません。また、オブジェクトを常に実際のものにするために、オブジェクトのプロパティの変更をリッスンすることもできません。

ただし、既存の動作が期待されることを確認する必要があります。

多くの不満が続き、最終的にはバグ修正につながりました。

2012年8月に実装されたパッチの変更ログノート(http://trac.webkit.org/changeset/125174):

現在のところ、オブジェクト(配列)をコンソールにダンプすると、コンソールオブジェクトの展開時にオブジェクトのプロパティが読み取られます(つまり、遅延します)。これは、変更中に同じオブジェクトをダンプすると、コンソールを使用してデバッグするのが困難になることを意味します。

この変更により、ロギングの瞬間にオブジェクト/配列の簡略化されたプレビューの生成が開始され、この情報がフロントエンドに渡されます。これは、フロントエンドがすでに開かれている場合にのみ発生します。これは、console.log()に対してのみ機能し、ライブコンソールの相互作用に対しては機能しません。

于 2014-06-12T04:51:05.057 に答える
23

2022年2月現在のMozillaからの最新のガイダンス:

使用しないでくださいconsole.log(obj)、使用してくださいconsole.log(JSON.parse(JSON.stringify(obj)))

objこのようにして、ログに記録した時点での値を確認できます。それ以外の場合、多くのブラウザは、値の変更に応じて常に更新されるライブビューを提供します。これはあなたが望むものではないかもしれません。

于 2019-11-15T23:10:32.170 に答える
13

配列はオブジェクトです。変数はオブジェクトを参照します。したがって、2番目のケースの割り当てでは、参照(アドレス)が配列に「A」から「C」にコピーされました。その後、両方の変数が同じ単一のオブジェクト(配列)を参照します。

数値などのプリミティブ値は、あなたのような単純な割り当てで、ある変数から別の変数に完全にコピーされます。「A++;」ステートメントは、「A」に新しい値を割り当てます。

別の言い方をすれば、変数の値は、プリミティブ値(数値、ブール値、、、またはnull文字列)の場合もあれば、オブジェクトへの参照の場合もあります。文字列プリミティブの場合は、プリミティブ(スカラー)値というよりもオブジェクトに似ているため少し奇妙ですが、不変であるため、数字のように見せかけてもかまいません。

于 2012-07-01T18:38:09.307 に答える
4

編集:以下の有用なコメントを保存するためだけにこの回答を保持します。

@Esailijaは実際には正しいです-console.log()変数をログに記録しようとしたときに変数が持っていた値を必ずしもログに記録するわけではありません。あなたの場合、への両方の呼び出しは、ソートconsole.log()にの値をログに記録します。C

問題のコードをコンソールで5つの個別のステートメントとして実行しようとすると、期待した結果が表示されます(最初、、、[2, 1]次に[1, 2])。

于 2012-07-01T18:41:11.473 に答える
2

すべての状況で機能するわけではありませんが、この問題を解決するために「ブレークポイント」を使用することになりました。

mysterious = {property:'started'}

// prints the value set below later ?
console.log(mysterious)

// break,  console above prints the first value, as god intended
throw new Error()

// later
mysterious = {property:'changed', extended:'prop'}
于 2012-08-26T04:24:02.283 に答える
0

この問題はSafariにも存在します。他の人がこの質問や同様の質問で指摘しているように、コンソールにはオブジェクトへの参照が渡され、コンソールが開かれたときのオブジェクトの値が出力されます。たとえば、コンソールでコードを直接実行すると、値は期待どおりに出力されます。JSONの文字列化の代わりに、配列(たとえば、console.log([... C]);)とオブジェクトを分散することを好みます。結果はまったく同じですが、コードは少しきれいに見えます。共有するVSコードスニペットが2つあります。

    "Print object value to console": {
      "prefix": "clo",
      "body": [
         "console.log(\"Spread object: \", {...$0});"
      ],
      "description": "Prints object value instead of reference to console, to avoid console.log async update"
   },
   "Print array value to console": {
      "prefix": "cla",
      "body": [
         "console.log(\"Spread array: \", [...$0]);"
      ],
      "description": "Prints array value instead of reference to console, to avoid console.log async update"
   }

console.log(JSON.parse(JSON.stringify(c)))と同じ出力を取得するために、必要に応じて文字列部分を省略できます。ちなみに、スプレッド構文は多くの場合、時間とコードを節約します。

于 2019-10-12T17:56:03.003 に答える