Dengan kode ini:
function baz() {
var x = "foo";
function bar() {
debugger;
};
bar();
}
baz();
Saya mendapatkan hasil yang tidak terduga ini:
Ketika saya mengubah kode:
function baz() {
var x = "foo";
function bar() {
x;
debugger;
};
bar();
}
Saya mendapatkan hasil yang diharapkan:
Juga, jika ada panggilan ke eval
dalam fungsi dalam, saya dapat mengakses variabel saya seperti yang saya ingin lakukan (tidak peduli apa yang saya berikan eval
).
Sementara itu, alat pengembang Firefox memberikan perilaku yang diharapkan dalam kedua keadaan.
Ada apa dengan Chrome yang berperilaku debugger kurang nyaman daripada Firefox? Saya telah mengamati perilaku ini selama beberapa waktu, hingga dan termasuk Versi 41.0.2272.43 beta (64-bit).
Apakah itu mesin javascript Chrome "meratakan" fungsi ketika itu bisa?
Menariknya jika saya menambahkan variabel kedua yang adalah direferensikan dalam fungsi batin, x
variabel masih terdefinisi.
Saya mengerti bahwa sering ada quirks dengan cakupan dan definisi variabel ketika menggunakan debugger interaktif, tetapi menurut saya berdasarkan spesifikasi bahasa seharusnya ada solusi "terbaik" untuk quirks ini. Jadi saya sangat ingin tahu apakah ini karena Chrome lebih mengoptimalkan daripada Firefox. Dan juga apakah optimasi ini dapat dengan mudah dinonaktifkan selama pengembangan (mungkin mereka harus dinonaktifkan ketika alat dev terbuka?).
Juga, saya dapat mereproduksi ini dengan breakpoints serta debugger
pernyataannya.
sumber
debugger;
garis itu sebenarnya tidak dipanggil dari dalambar
. Jadi lihat jejak stack ketika ia berhenti di debugger: Apakahbar
fungsi disebutkan dalam stacktrace? Jika saya benar, maka stacktrace seharusnya mengatakan itu dijeda pada baris 5, pada baris 7, pada baris 9.temp1
dilampirkan ke konsol dan Anda dapat menggunakannya untuk mengakses entri lingkup.Jawaban:
Saya telah menemukan laporan masalah v8 yang tepatnya tentang apa yang Anda minta.
Sekarang, Untuk meringkas apa yang dikatakan dalam laporan masalah itu ... v8 dapat menyimpan variabel yang lokal ke fungsi di stack atau dalam objek "konteks" yang hidup di heap. Ini akan mengalokasikan variabel lokal pada stack selama fungsi tersebut tidak mengandung fungsi bagian dalam yang merujuk padanya. Ini adalah optimasi . Jika ada fungsi dalam yang merujuk ke variabel lokal, variabel ini akan diletakkan di objek konteks (yaitu di heap, bukan di stack). Kasus
eval
khusus: jika dipanggil sama sekali oleh fungsi dalam, semua variabel lokal dimasukkan ke dalam objek konteks.Alasan untuk objek konteks adalah bahwa secara umum Anda bisa mengembalikan fungsi dalam dari yang luar dan kemudian tumpukan yang ada sementara fungsi luar berlari tidak akan tersedia lagi. Jadi apapun yang diakses fungsi bagian dalam harus selamat dari fungsi luar dan hidup di heap daripada di stack.
Debugger tidak dapat memeriksa variabel-variabel yang ada di tumpukan. Mengenai masalah yang ditemukan dalam debugging, salah satu Anggota Proyek mengatakan :
Berikut adalah contoh dari "jika ada fungsi dalam merujuk pada variabel, letakkan di objek konteks". Jika Anda menjalankan ini, Anda akan dapat mengakses
x
padadebugger
pernyataan meskipunx
hanya digunakan dalamfoo
fungsi, yang tidak pernah dipanggil !sumber
Seperti @Louis mengatakan itu disebabkan oleh optimasi v8. Anda dapat melintasi tumpukan panggilan ke bingkai tempat variabel ini terlihat:
Atau ganti
debugger
denganeval
akan membatalkan potongan saat inisumber
debugger
, dan konteks memang tersedia. Jika Anda meningkatkan tumpukan satu tingkat ke kode yang sebenarnya Anda coba debug, Anda kembali ke tidak memiliki akses ke konteks. Jadi itu hanya sedikit kikuk, tidak bisa melihat kode yang Anda debug saat mengakses variabel penutupan tersembunyi. Namun, saya akan lebih memilih, karena ini menyelamatkan saya dari keharusan menambahkan kode yang tidak jelas untuk debugging, dan itu memberi saya akses ke seluruh konteks tanpa menonaktifkan seluruh aplikasi.eval
jendela sumber ed kuning untuk mendapatkan akses ke konteks: Anda tidak dapat melangkah melalui kode (kecuali jika Anda meletakkan dieval('debugger')
antara semua baris yang ingin Anda lewati.)controllers.forEach(c => c.update())
dan mencapai breakpoint di suatu tempat jauh di dalamc.update()
. Jika saya kemudian memilih bingkai di manacontrollers.forEach()
dipanggil,controllers
tidak terdefinisi (tetapi semua yang lain dalam bingkai itu terlihat). Saya tidak dapat mereproduksi dengan versi minimal, saya kira mungkin ada ambang batas kompleksitas yang perlu dilewati atau sesuatu.somewhere deep inside c.update()
kode Anda menjadi async dan Anda melihat bingkai tumpukan asyncSaya juga memperhatikan ini di nodejs. Saya percaya (dan saya akui ini hanya tebakan) bahwa ketika kode dikompilasi, jika
x
tidak muncul di dalambar
, itu tidakx
tersedia di dalam lingkupbar
. Ini mungkin membuatnya sedikit lebih efisien; masalahnya adalah seseorang lupa (atau tidak peduli) bahwa bahkan jika tidak adax
dibar
, Anda mungkin memutuskan untuk menjalankan debugger dan karenanya masih perlu aksesx
dari dalambar
.sumber
eval
perintah yang tidak terkait . Jika variabel dideklarasikan, itu harus dapat diakses.Wow, sangat menarik!
Seperti yang disebutkan orang lain, ini sepertinya berkaitan dengan
scope
, tetapi lebih khusus, berkaitan dengandebugger scope
. Ketika skrip yang disuntikkan dievaluasi dalam alat pengembang, sepertinya akan menentukan aScopeChain
, yang menghasilkan quirkiness (karena terikat pada lingkup inspektur / debugger). Variasi dari apa yang Anda poskan adalah ini:(EDIT - sebenarnya, Anda menyebutkan ini dalam pertanyaan awal Anda, ya, salahku! )
Untuk yang ambisius dan / atau ingin tahu, lingkup (heh) keluar sumber untuk melihat apa yang terjadi:
https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger
sumber
Saya menduga ini ada hubungannya dengan variabel dan fungsi mengangkat. JavaScript membawa semua deklarasi variabel dan fungsi ke bagian atas fungsi tempat mereka didefinisikan. Info lebih lanjut di sini: http://jamesallardice.com/explaining-function-and-variable-hoisting-in-javascript/
Saya bertaruh bahwa Chrome memanggil break point dengan variabel tidak tersedia untuk lingkup karena tidak ada yang lain dalam fungsi. Ini sepertinya berhasil:
Seperti ini:
Semoga ini, dan / atau tautan di atas membantu. Ini adalah jenis pertanyaan SO favorit saya, BTW :)
sumber