web-dev-qa-db-ja.com

ChromeのJavaScriptコンソールは配列の評価について怠zyですか?

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

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

シンプルでしょ?これに対して、Firebugは次のように述べています。

["hi"]
["bye"]

すばらしいですが、ChromeのJavaScriptコンソール(7.0.517.41ベータ版)は次のように述べています:

["bye"]
["bye"]

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

enter image description here

113
Eric Mickelsen

コメントありがとう、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
58
Eric Mickelsen

Ericの説明から、それは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
17

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.logsを使用する場合よりもさらに、デバッグの完了後にそれらを削除する必要があります。

6
yingted

これはWebkitでパッチされていますが、Reactフレームワークを使用する場合、状況によってはこれが発生します。

console.log(JSON.stringify(the_array));
2
justinsAccount

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

logwarn、およびerrorメソッドのみを実装します。通常のconsoleと交換可能にするためには、さらに追加する必要があります。

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);
1
wrygiel

Chromeは、 "pre compile"フェーズで、 "s"のインスタンスをpointerで実際の配列に置き換えています。

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;
}
0
Shadow Wizard