Apakah konsol JavaScript Chrome malas mengevaluasi array?

128

Saya akan mulai dengan kode:

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

Sederhana bukan? Menanggapi ini, Firebug mengatakan:

["hi"]
["bye"]

Luar biasa, tetapi konsol JavaScript Chrome (7.0.517.41 beta) mengatakan:

["bye"]
["bye"]

Apakah saya telah melakukan kesalahan, atau apakah konsol JavaScript Chrome terlalu malas mengevaluasi array saya?

masukkan deskripsi gambar di sini

Eric Mickelsen
sumber
1
Saya mengamati perilaku yang sama di Safari - jadi mungkin ini adalah masalah webkit. Cukup mengejutkan. Saya akan menyebutnya bug.
Lee
7
Bagi saya itu terlihat seperti serangga. Pada Linux Opera dan Firefox menampilkan hasil yang diharapkan, Chrome dan browser berbasis Webkit lainnya tidak. Anda mungkin ingin melaporkan masalah ini ke Webkit devs: webkit.org/quality/reporting.html
tec
2
per Maret 2016, masalah ini tidak ada lagi.
kmonsoor
1
April 2020, mengalami masalah ini di Chrome. Membuang waktu 2 jam mencari bug di kode saya yang ternyata bug di Chrome.
The Fox
1
Juga perlu diperhatikan bahwa itooltip ikon biru mengatakan "Nilai di bawah baru saja dievaluasi".
pengguna4642212

Jawaban:

70

Terima kasih atas komentarnya, tec. Saya dapat menemukan bug Webkit yang belum dikonfirmasi yang menjelaskan masalah ini: https://bugs.webkit.org/show_bug.cgi?id=35801 (EDIT: sekarang sudah diperbaiki!)

Tampaknya ada beberapa perdebatan mengenai seberapa banyak bug itu dan apakah itu dapat diperbaiki. Bagi saya, ini sepertinya perilaku yang buruk. Ini sangat mengganggu saya karena, setidaknya di Chrome, ini terjadi ketika kode berada dalam skrip yang dijalankan segera (sebelum halaman dimuat), bahkan ketika konsol terbuka, setiap kali halaman di-refresh. Memanggil console.log saat konsol belum aktif hanya menghasilkan referensi ke objek yang diantrekan, bukan keluaran yang akan dimuat di konsol. Oleh karena itu, array (atau objek apa pun), tidak akan dievaluasi hingga konsol siap. Ini benar-benar kasus evaluasi malas.

Namun, ada cara sederhana untuk menghindari hal ini di kode Anda:

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

Dengan memanggil toString, Anda membuat representasi dalam memori yang tidak akan diubah dengan pernyataan berikut, yang akan dibaca konsol ketika sudah siap. Output konsol sedikit berbeda dengan meneruskan objek secara langsung, tetapi tampaknya dapat diterima:

hi
bye
Eric Mickelsen
sumber
1
Sebenarnya, dengan array asosiatif atau objek lain, ini bisa menjadi masalah nyata, karena toString tidak menghasilkan apa pun yang bernilai. Apakah ada penyelesaian yang mudah untuk objek secara umum?
Eric Mickelsen
29
JSON.stringify ()
draeton
1
webkit mendapatkan patch untuk ini beberapa bulan yang lalu
antony.trupe
1
lakukan ini: console.log (JSON.parse (JSON.stringify (s));
Lee Comstock
Saya hanya ingin menyebutkan bahwa dalam versi Chrome saat ini konsol tertunda dan nilai keluaran salah lagi (atau pernah benar). Misalnya, saya sedang men-logging sebuah array dan memunculkan nilai teratas setelah memasukkannya, tetapi itu muncul tanpa nilai yang muncul. Saran toString () Anda sangat membantu untuk mencapai tempat yang saya perlukan untuk melihat nilainya.
Nicholas R. Grant
21

Dari penjelasan Eric, itu karena console.log() antrian, dan mencetak nilai selanjutnya dari array (atau objek).

Ada 5 solusi:

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
nonopolaritas
sumber
7

Anda dapat mengkloning sebuah array dengan Array#slice:

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

Fungsi yang dapat Anda gunakan sebagai ganti console.logyang tidak memiliki masalah ini adalah sebagai berikut:

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

Untuk kasus objek, sayangnya, metode terbaik tampaknya men-debug terlebih dahulu dengan browser non-WebKit, atau menulis fungsi rumit untuk dikloning. Jika Anda hanya bekerja dengan objek sederhana, di mana urutan tombol tidak menjadi masalah dan tidak ada fungsi, Anda selalu dapat melakukan:

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

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

Semua metode ini jelas sangat lambat, jadi bahkan lebih daripada dengan metode normal console.log, Anda harus menghapusnya setelah Anda selesai debugging.

yingted
sumber
2

Ini telah ditambal di Webkit, namun ketika menggunakan kerangka kerja React, ini terjadi pada saya dalam beberapa keadaan, jika Anda memiliki masalah seperti itu, gunakan saja seperti yang disarankan orang lain:

console.log(JSON.stringify(the_array));
justinsAccount
sumber
2
Bisa konfirmasi. Ini benar-benar yang terburuk ketika mencoba keluar dari ReactSyntheticEvents. Bahkan aJSON.parse(JSON.stringify(event)) tidak mendapatkan kedalaman / akurasi yang tepat. Pernyataan debugger adalah satu-satunya solusi nyata yang saya temukan untuk mendapatkan wawasan yang benar.
CStumph
1

Ini sudah dijawab, tetapi saya akan membatalkan jawaban saya. Saya menerapkan pembungkus konsol sederhana yang tidak mengalami masalah ini. Membutuhkan jQuery.

Ini hanya mengimplementasikan log , warndan errormetode, Anda harus menambahkan lebih banyak agar dapat dipertukarkan dengan reguler 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);
wrygiel.dll
sumber
0

Sepertinya Chrome sedang mengganti dalam fase "pra-kompilasi" setiap instance "s" dengan pointer ke larik sebenarnya.

Salah satu cara mengatasinya adalah dengan mengkloning larik, sebagai gantinya mencatat salinan baru:

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;
}
Shadow Wizard adalah Ear For You
sumber
Itu bagus, tapi karena itu salinan yang dangkal, masih ada kemungkinan masalah yang lebih halus. Dan bagaimana dengan objek yang bukan array? (Itu adalah masalah sebenarnya sekarang.) Saya rasa apa yang Anda katakan tentang "pra kompilasi" tidak akurat. Juga, ada kesalahan dalam kode: clone [clone.length] harus clone [i].
Eric Mickelsen
Tidak ada kesalahan, saya telah menjalankannya dan tidak apa-apa. clone [clone.length] persis seperti clone [i], karena array dimulai dengan panjang 0, dan begitu juga dengan iterator loop "i". Bagaimanapun, tidak yakin bagaimana itu akan berperilaku dengan objek yang kompleks tapi IMO patut dicoba. Seperti yang saya katakan, itu bukan solusi, ini adalah cara mengatasi masalah ..
Shadow Wizard adalah Ear For You
@Shadow Wizard: Poin yang bagus: clone.length akan selalu sama dengan i. Ini tidak akan bekerja untuk objek. Mungkin ada solusi dengan "untuk masing-masing".
Eric Mickelsen
Benda yang kamu maksud ini? var s = {param1: "hi", param2: "apa kabar?" }; jika demikian saya baru saja menguji dan ketika Anda memiliki s ["param1"] = "bye"; itu bekerja dengan baik seperti yang diharapkan. Bisakah Anda memposting contoh "itu tidak akan berfungsi untuk objek"? Saya akan melihat dan mencoba mendaki yang itu juga.
Shadow Wizard adalah Ear For You
@Shadow Wizard: Jelas sekali, fungsi Anda akan gagal menggandakan properti dan tidak akan berfungsi pada objek apa pun tanpa properti length. Bug webkit memengaruhi semua objek, tidak hanya array.
Eric Mickelsen
0

solusi terpendek sejauh ini adalah menggunakan sintaks array atau penyebaran objek untuk mendapatkan klon nilai yang akan dipertahankan seperti pada saat logging, yaitu:

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

Namun berhati-hatilah karena ini melakukan penyalinan yang dangkal, sehingga nilai non-primitif bersarang yang dalam tidak akan dikloning dan karenanya ditampilkan dalam status modifikasinya di konsol

ptica
sumber