153

私はコードから始めます:

var s = ["hi"];
console.log(s);
s[0] = "bye";
console.log(s);

シンプルですね。これに対して、Firefox コンソールには次のように表示されます。

[ "hi" ]
[ "bye" ]

素晴らしいですが、Chrome の JavaScript コンソール (7.0.517.41 ベータ版) には次のように書かれています。

[ "bye" ]
[ "bye" ]

何か間違ったことをしたのでしょうか、それとも Chrome の JavaScript コンソールが配列の評価を非常に怠っているのでしょうか?

説明されている動作を示すコンソールのスクリーンショット。

4

7 に答える 7

85

tecさん、コメントありがとうございます。この問題を説明する既存の未確認の Webkit バグを見つけることができました: https://bugs.webkit.org/show_bug.cgi?id=35801 (編集: 現在修正済み!)

それがどの程度のバグであり、修正可能かどうかについては、いくつかの議論があるようです. 私には悪い振る舞いのように思えます。少なくとも Chrome では、ページが更新されるたびに、コンソールが開いていても、すぐに (ページが読み込まれる前に) 実行されるスクリプトにコードが存在する場合に発生するため、特に厄介でした。コンソールがまだアクティブになっていないときに console.log を呼び出すと、コンソールに含まれる出力ではなく、キューに入れられているオブジェクトへの参照が返されるだけです。したがって、配列 (または任意のオブジェクト) は、コンソールの準備が整うまで評価されません。これはまさに遅延評価のケースです。

ただし、コードでこれを回避する簡単な方法があります。

var s = ["hi"];
console.log(s.toString());
s[0] = "bye";
console.log(s.toString());

toString を呼び出すことにより、次のステートメントによって変更されない表現をメモリ内に作成し、準備ができたときにコンソールが読み取ります。コンソール出力は、オブジェクトを直接渡す場合とは少し異なりますが、許容できるようです。

hi
bye
于 2010-10-30T18:54:45.090 に答える
27

エリックの説明によると、それはconsole.log()キューに入れられているためであり、配列 (またはオブジェクト) の後の値を出力します。

5 つのソリューションが考えられます。

1. arr.toString()   // not well for [1,[2,3]] as it shows 1,2,3
2. arr.join()       // same as above
3. arr.slice(0)     // a new array is created, but if arr is [1, 2, arr2, 3] 
                    //   and arr2 changes, then later value might be shown
4. arr.concat()     // a new array is created, but same issue as slice(0)
5. JSON.stringify(arr)  // works well as it takes a snapshot of the whole array 
                        //   or object, and the format shows the exact structure
于 2012-10-07T22:11:56.767 に答える
7

次を使用して配列を複製できますArray#slice

console.log(s); // ["bye"], i.e. incorrect
console.log(s.slice()); // ["hi"], i.e. correct

代わりに使用できるconsole.logこの問題のない関数は次のとおりです。

console.logShallowCopy = function () {
    function slicedIfArray(arg) {
        return Array.isArray(arg) ? arg.slice() : arg;
    }

    var argsSnapshot = Array.prototype.map.call(arguments, slicedIfArray);
    return console.log.apply(console, argsSnapshot);
};

残念ながら、オブジェクトの場合、WebKit 以外のブラウザーで最初にデバッグするか、複雑な関数を記述して複製するのが最善の方法のようです。キーの順序が重要ではなく、関数がない単純なオブジェクトのみを操作している場合は、いつでも次のことができます。

console.logSanitizedCopy = function () {
    var args = Array.prototype.slice.call(arguments);
    var sanitizedArgs = JSON.parse(JSON.stringify(args));

    return console.log.apply(console, sanitizedArgs);
};

これらのメソッドはすべて明らかに非常に遅いため、通常console.logの s よりもさらに遅いので、デバッグが完了したらそれらを取り除かなければなりません。

于 2011-01-27T17:42:49.807 に答える
4

これは Webkit でパッチされていますが、React フレームワークを使用している場合、状況によってはこれが発生します。そのような問題がある場合は、他の人が提案するように使用してください。

console.log(JSON.stringify(the_array));
于 2015-04-30T00:35:52.160 に答える
1

これはすでに回答されていますが、とにかく回答を削除します。この問題の影響を受けない単純なコンソール ラッパーを実装しました。jQuery が必要です。

とメソッドのみlogを実装します。通常の と交換できるようにするには、さらにいくつか追加する必要があります。warnerrorconsole

var fixedConsole;
(function($) {
    var _freezeOne = function(arg) {
        if (typeof arg === 'object') {
            return $.extend(true, {}, arg);
        } else {
            return arg;
        }
    };
    var _freezeAll = function(args) {
        var frozen = [];
        for (var i=0; i<args.length; i++) {
            frozen.push(_freezeOne(args[i]));
        }
        return frozen;
    };
    fixedConsole = {
        log: function() { console.log.apply(console, _freezeAll(arguments)); },
        warn: function() { console.warn.apply(console, _freezeAll(arguments)); },
        error: function() { console.error.apply(console, _freezeAll(arguments)); }
    };
})(jQuery);
于 2013-02-28T15:31:31.600 に答える
0

Chrome は、「プリコンパイル」フェーズで、「s」のインスタンスを実際の配列へのポインタに置き換えているようです。

1 つの方法は、アレイのクローンを作成し、代わりに新しいコピーをログに記録することです。

var s = ["hi"];
console.log(CloneArray(s));
s[0] = "bye";
console.log(CloneArray(s));

function CloneArray(array)
{
    var clone = new Array();
    for (var i = 0; i < array.length; i++)
        clone[clone.length] = array[i];
    return clone;
}
于 2010-10-31T14:20:52.530 に答える