Chrome hata ayıklayıcısı neden kapalı yerel değişkenin tanımsız olduğunu düşünüyor?


167

Bu kodla:

function baz() {
  var x = "foo";

  function bar() {
    debugger;
  };
  bar();
}
baz();

Bu beklenmedik sonucu alıyorum:

resim açıklamasını buraya girin

Kodu değiştirdiğimde:

function baz() {
  var x = "foo";

  function bar() {
    x;
    debugger;
  };
  bar();
}

Beklenen sonucu alıyorum:

resim açıklamasını buraya girin

Ayrıca, evaliç işlev içinde herhangi bir çağrı varsa, değişkenime yapmak istediğim gibi erişebilirim (ne ilettiğim önemli değil eval).

Bu arada, Firefox dev araçları her iki durumda da beklenen davranışı verir.

Hata ayıklayıcının Firefox'tan daha az uygun davrandığı Chrome'da ne var? Bu davranışı 41.0.2272.43 beta (64-bit) sürümüne kadar bir süredir gözlemledim.

Chrome'un javascript motoru işlevleri mümkün olduğunca "düzleştirir" mi?

İkinci bir değişken eklemek İlginçtir eğer bir iç fonksiyonu başvurulan, xdeğişken hala tanımlanmamış.

Etkileşimli bir hata ayıklayıcı kullanırken genellikle kapsam ve değişken tanımlamalı tuhaflıklar olduğunu anlıyorum, ancak bana öyle geliyor ki, dil spesifikasyonuna dayanarak bu tuhaflıklar için "en iyi" bir çözüm olmalı. Bunun Chrome'un Firefox'tan başka optimizasyondan kaynaklanıp kaynaklanmadığını merak ediyorum. Ayrıca bu optimizasyonların geliştirme sırasında kolayca devre dışı bırakılıp bırakılamayacağı (belki geliştirici araçları açıkken devre dışı bırakılmaları gerekir?).

Ayrıca, bunu kesme noktalarının yanı sıra debuggerifade ile de çoğaltabilirim .


2
belki sizin için kullanılmayan değişkenleri sizin
yerinizden alıyor

markle976, debugger;hattın aslında içeriden çağrılmadığını söylüyor gibi görünüyor bar. Bu nedenle, hata ayıklayıcıda durakladığında yığın izlemeye bakın: Yığın izinde barbelirtilen işlev mi? Yanılmıyorsam, o zaman StackTrace hat 9. at, çizgi 7'de, hat 5 de durdurulmuş oluyor demeliyim
David Knipe

V8 düzleştirme fonksiyonları ile ilgisi olduğunu düşünmüyorum. Bence bu sadece bir tuhaflık; Buna böcek bile diyebilir miyim bilmiyorum. Bence David'in aşağıdaki cevabı en mantıklı.
markle976


2
Aynı sorunum var, nefret ediyorum. Ancak konsolda erişim kapatma girişlerine sahip olmam gerektiğinde, kapsamı görebileceğiniz yere gidiyorum, Closure girişini bulup açıyorum . Ardından ihtiyacınız olan öğeye sağ tıklayın ve Global Değişken Olarak Kaydet'e tıklayın . temp1Konsola yeni bir genel değişken eklenir ve bunu kapsam girişine erişmek için kullanabilirsiniz.
Pablo

Yanıtlar:


149

Tam olarak sorduğunuz şeyle ilgili bir v8 sorun raporu buldum .

Şimdi, bu sorun raporunda söylenenleri özetlemek için ... v8 yığındaki bir işlev için yerel olan veya yığın üzerinde yaşayan bir "bağlam" nesnesindeki değişkenleri saklayabilir . İşlev, bunlara başvuran herhangi bir iç işlev içermediği sürece yığın üzerinde yerel değişkenler atar. Bu bir optimizasyon . Herhangi bir iç işlev yerel bir değişkene başvuruyorsa, bu değişken bir bağlam nesnesine (yani yığın yerine yığın üzerine) konur. Özel durum eval: iç işlev tarafından çağrılırsa, tüm yerel değişkenler bağlam nesnesine konur.

Bağlam nesnesinin nedeni, genel olarak bir iç işlevi dış işlevden döndürebilmeniz ve daha sonra dış işlev koşarken var olan yığının artık kullanılamayacağıdır. Bu nedenle, iç işlevin eriştiği her şey dış işlevden sağ kalmalı ve yığın yerine yığın üzerinde yaşamalıdır.

Hata ayıklayıcı, yığında bulunan değişkenleri denetleyemez. Hata ayıklamada karşılaşılan sorunla ilgili olarak, bir Proje Üyesi şunları söylüyor :

Düşünebildiğim tek çözüm, devtools ne zaman açık olursa, tüm kodu deopt edip zorunlu bağlam tahsisi ile yeniden derleyeceğimizdir. Bu, devtools etkinken performansı önemli ölçüde geriledi.

Aşağıda, "herhangi bir iç işlev değişkene başvuruyorsa, onu bir bağlam nesnesine koy" örneğine bir örnek verilmiştir. Bunu erişmek mümkün olacak çalıştırırsanız xde debuggerolsa açıklamada xsadece kullanılan foofonksiyonu, asla denir !

function baz() {
  var x = "x value";
  var z = "z value";

  function foo () {
    console.log(x);
  }

  function bar() {
    debugger;
  };

  bar();
}
baz();

13
Kodu deopt etmenin bir yolunu buldunuz mu? Hata ayıklayıcı bir REPL ve kod orada kullanmak sonra kodu kendi dosyalarıma aktarmak istiyorum. Ancak, olması gereken değişkenlerin erişilebilir olmadığı için genellikle uygun değildir. Basit bir değerlendirme yapmaz. Döngü için sonsuz bir duydum.
Ray Foss

Aslında kodu deopt yolları aramadım bu yüzden hata ayıklama sırasında bu sorunla karşılaşmadım.
Louis

6
Sorunun son yorumu şöyle diyor: V8'i her şeyin zorla bağlam tahsis edildiği bir moda koymak mümkündür, ancak Devtools UI aracılığıyla bunu nasıl / ne zaman tetikleyeceğimi bilmiyorum Hata ayıklama uğruna bazen yapmak istiyorum . Böyle bir modu nasıl zorlayabilirim?
Suma

2
@ user208769 Yinelenen olarak kapatırken, gelecekteki okuyucular için en faydalı soruyu destekliyoruz. Hangi sorunun en yararlı olduğunu belirlemeye yardımcı olan birden fazla faktör vardır: sorunuz tam olarak 0 cevap alırken, bu soruya birden fazla puan verildi. Yani bu soru ikisinin en faydalısı. Tarihler, sadece yararlılık çoğunlukla eşitse belirleyici bir faktör haline gelir.
Louis

1
Bu cevap asıl soruyu (Neden?), Ancak zımni soruyu yanıtlar - Koduma kendilerine fazladan referans eklemeden hata ayıklama için kullanılmayan bağlam değişkenlerine nasıl erişebilirim? - aşağıdaki @OwnageIsMagic tarafından daha iyi cevaplanmıştır.
Sigfried

30

@ Louis gibi dedi v8 optimizasyon neden oldu. Çağrı yığınını, bu değişkenin görülebileceği çerçeveye taşıyabilirsiniz:

call1 call2

Veya yerine debuggersahip

eval('debugger');

eval geçerli yığın deopt edecek


1
Neredeyse harika! Bir VM modülünde (sarı) içeriklerle duraklar debuggerve bağlam gerçekten de kullanılabilir. Yığını, gerçekten hata ayıklamaya çalıştığınız koda bir adım yükseltirseniz, bağlama erişiminiz olmaz. Bu nedenle, gizli kapatma değişkenlerine erişirken hata ayıkladığınız koda bakamamanız biraz karmaşık. Yine de, hata ayıklama için açık olmayan bir kod eklemek zorunda kalmamamı sağladığım ve tüm uygulamanın deoptimize edilmeden tüm içeriğe erişebilmemi sağladığım için oy kullanacağım.
Sigfried

Oh ... evaliçeriğe erişmek için sarı ed kaynak penceresini kullanmaktan bile daha karmaşık: koddan geçemezsiniz (adım atmak eval('debugger')istediğiniz tüm satırlar arasına koymazsanız )
Sigfried

Uygun yığın çerçevesine geçtikten sonra bile bazı değişkenlerin görünmez olduğu durumlar olduğu görülmektedir; Gibi controllers.forEach(c => c.update())bir şey var ve içinde derin bir yerde bir kırılma noktasına çarptı c.update(). Daha sonra controllers.forEach()çağrılan kareyi controllersseçersem tanımsızdır (ancak bu karedeki diğer her şey görünür). Minimal bir versiyonla çoğaltamadım, sanırım geçilmesi gereken bazı karmaşıklık eşiği olabilir.
PeterT

@PeterT <undefined> ise yanlış yerdesiniz Veya somewhere deep inside c.update()kodunuz zaman uyumsuz oluyor ve zaman uyumsuz yığın çerçevesi görüyorsunuz
OwnageIsMagic

6

Bunu nodejslerde de fark ettim. Ben inanıyorum (ve bu sadece bir tahmin olduğunu itiraf) kod derlendiğinde, xiçinde görünmüyorsa , kapsamı içinde kullanılabilir baryapmaz . Bu muhtemelen biraz daha verimli hale getirir; Sorun birisi hiç olmasa bile unuttum (veya umursamadı) 'dir içinde , hata ayıklayıcı çalıştırabilir ve dolayısıyla hala erişime gerek karar verebilirsiniz içeriden .xbarxbarxbar


3
Teşekkürler. Temelde bunu javascript yeni başlayanlara "Hata ayıklayıcı yalan" daha iyi açıklamak istiyorum.
Gabe Kopley

@GabeKopley: Teknik olarak hata ayıklayıcı yalan söylemiyor. Bir değişkene başvurulmazsa, teknik olarak ekli değildir. Bu yüzden tercümanın kapanışı yaratmasına gerek yoktur.
slebetman

7
Konu o değil. Hata ayıklayıcı kullanırken, bir değişkenin dış kapsamdaki değerini bilmek istediğim bir durumdaydım, ancak bu nedenle yapamadım. Ve daha felsefi bir kayda göre, hata ayıklayıcının yalan söylediğini söyleyebilirim. Değişkenin iç kapsamda var olup olmadığı, gerçekte kullanılıp kullanılmadığına veya ilgisiz bir evalkomut olup olmadığına bağlı olmamalıdır . Değişken bildirilirse erişilebilir olmalıdır.
David Knipe

2

Vay canına, gerçekten ilginç!

Diğerlerinin de belirttiği gibi, bunun ilgili olduğu scope, ancak daha spesifik olarak ilişkili olduğu görülmektedir debugger scope. Enjekte edilen komut dosyası geliştirici araçlarında değerlendirildiğinde, ScopeChainbazı tuhaflıklarla sonuçlanan (müfettiş / hata ayıklayıcı kapsamına bağlı olduğu için) a'nın olduğu anlaşılıyor. Gönderdiğiniz öğelerin bir varyasyonu şudur:

(DÜZENLE - aslında, bunu orijinal sorunuzda söylüyorsunuz, evet , benim hatam! )

function foo() {
  var x = "bat";
  var y = "man";

  function bar() {
    console.log(x); // logs "bat"

    debugger; // Attempting to access "y" throws the following
              // Uncaught ReferenceError: y is not defined
              // However, x is available in the scopeChain. Weird!
  }
  bar();
}
foo();

Hırslı ve / veya meraklı için, neler olup bittiğini görmek için kaynağı dışarı çıkarın (heh):

https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/debugger


0

Bunun değişken ve fonksiyon kaldırma ile ilgili olduğundan şüpheleniyorum. JavaScript, tüm değişken ve işlev bildirimlerini tanımlandıkları işlevin üstüne getirir. Daha fazla bilgi için: http://jamesallardice.com/explaining-function-and-variable-hoisting-in-javascript/

Eminim Chrome, işlevde başka bir şey olmadığı için değişkenin kapsamı için kullanılamayan kesme noktasını çağırıyor. Bu işe yarıyor gibi görünüyor:

function baz() {
  var x = "foo";

  function bar() {
    console.log(x); 
    debugger;
  };
  bar();
}

Bunun gibi:

function baz() {
  var x = "foo";

  function bar() {
    debugger;
    console.log(x);     
  };
  bar();
}

Umarım ve / veya yukarıdaki bağlantı yardımcı olur. Bunlar benim en sevdiğim SO soruları, BTW :)


Teşekkürler! :) FF'nin farklı şekilde ne yaptığını merak ediyorum. Bir geliştirici olarak benim bakış açımdan, FF deneyimi objektif olarak daha iyi ...
Gabe Kopley

2
"lex zamanında kırılma noktası çağırmak" şüpheliyim. Kesme noktaları bunun için değildir. Ve işlevde başka şeylerin olmamasının neden önemli olduğunu anlamıyorum. Söyledikten sonra, eğer nodejs gibi bir şey varsa, kesme noktaları çok buggy olabilir.
David Knipe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.