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


126

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

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コンソールは私の配列の評価について非常に怠惰ですか?

ここに画像の説明を入力してください


1
私はSafariでも同じ動作を観察しているので、おそらくWebkitの問題です。かなり意外です。私はそれをバグと呼びます。
Lee

7
私にはそれはバグのように見えます。Linux OperaとFirefoxでは期待どおりの結果が表示されますが、Chromeとその他のWebkitベースのブラウザーでは表示されません。この問題をWebkit 開発者
tec

2
2016年3月現在、この問題は解消されています。
kmonsoor 2016年

1
2020年4月、Chromeでこの問題が発生しました。Chromeのバグであることが判明した私のコードのバグを探すために2時間を無駄にしました。
フォックス

1
また、青いiアイコンのツールチップには、「以下の値はたった今評価されました。」と表示されています。
user4642212

回答:


69

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

1
実際、連想配列やその他のオブジェクトでは、toStringは何の値も生成しないため、これは実際の問題になる可能性があります。一般的なオブジェクトの簡単な回避策はありますか?
Eric Mickelsen、

29
JSON.stringify()
draeton

1
webkitが数か月前にこのためのパッチをリリースしました
antony.trupe

1
これを行います:console.log(JSON.parse(JSON.stringify(s));
Lee Comstock

現在のChromeバージョンでは、コンソールが遅延し、値が正しく出力されない(または正しく表示されていた)ことをお伝えしておきます。たとえば、配列をログに記録し、それをログに記録した後に最上位の値をポップしていましたが、ポップされた値なしで表示されていました。あなたのtoString()の提案は、私が値を見るために必要な場所に行くのに本当に役立ちました。
ニコラスR.グラント

21

エリックの説明から、それは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

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を使用する場合よりも、デバッグが完了した後でそれらを取り除く必要があります。


2

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

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

2
確認できます。これは、ReactSyntheticEventsをログアウトしようとするときに文字通り最悪です。でもJSON.parse(JSON.stringify(event))適切な深さ/精度が得られません。デバッガーのステートメントは、正しい洞察を得るために私が見つけた唯一の実際のソリューションです。
CStumph 2015

1

これは既に回答済みですが、とにかく回答を破棄します。この問題の影響を受けないシンプルなコンソールラッパーを実装しました。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);

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;
}

それは良いことですが、浅いコピーであるため、さらに微妙な問題が発生する可能性があります。そして、配列ではないオブジェクトはどうですか?(これらは現在、本当の問題です。)「プリコンパイル」についてあなたが言っていることは正確ではないと思います。また、コードにエラーがあります:clone [clone.length]はclone [i]である必要があります。
Eric Mickelsen、

エラーはなく、実行しましたが、問題はありませんでした。clone [clone.length]は、配列が長さ0で始まり、ループイテレータ "i"も同様であるため、clone [i]とまったく同じです。とにかく、複雑なオブジェクトでどのように動作するかはわかりませんが、IMOは試してみる価値があります。私は解決策ではないこと、言ったように、それは問題を回避する方法です...
シャドウウィザードはあなたのための耳である

@シャドウウィザード:良い点:clone.lengthは常にiに等しくなります。オブジェクトに対しては機能しません。おそらく「for each」を使用したソリューションがあります。
Eric Mickelsen、2010年

これが意味するオブジェクト?var s = {param1: "hi"、param2: "元気ですか?" }; もしそうなら、私はテストしたばかりで、s ["param1"] = "bye"; 期待どおりに正常に動作しています。「オブジェクトでは機能しない」の例を投稿していただけませんか?私もそれを見て登ろうとします。
シャドウウィザードはあなたのための耳です

@シャドウウィザード:明らかに、関数はプロパティの複製に失敗し、長さプロパティのないオブジェクトでは機能しません。Webkitのバグは、配列だけでなく、すべてのオブジェクトに影響します。
Eric Mickelsen、2010年

0

これまでの最短の解決策は、配列またはオブジェクトスプレッド構文を使用して、ロギング時に保持される値のクローンを取得することです。

console.log({...myObject});
console.log([...myArray]);

ただし、浅いコピーを行うため、警告が表示されます。そのため、深くネストされた非プリミティブ値は複製されず、変更された状態でコンソールに表示されません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.