Tüm Değişkenleri Kapsama Almak


194

Şu anda kapsamda olan tüm değişkenleri javascript almanın bir yolu var mı?


Camsoft'a cevabınız: Bu tamamen farklı bir soru; Cevabımı ele almak için cevabımı güncelledim.
TJ Crowder

3
Bu sadece genel bir soru, daha spesifik olmayacak çünkü kötü belgelere sahip belirsiz bir API ile çalışıyorum.
Ian

Global değişkenleri kastediyorsunuz! Sen görüntüleyebilir enumerable kullanarak küresel değişkenleri for (v in this) alert(v);. Yine de tüm küreseller numaralandırılamaz ve numaralandırılamayanların bir listesini almanın standart bir yolunu bilmiyorum.
Jason Orendorff

2
@ Jason - Hayır, soru açık. Bir fonksiyonu İçinde kapsamında değişkenler global değişkenler içerecektir this, arguments, parametreleri ve kapsamları kapsayan tanımlanan tüm değişkenler.
Tim Down

2
Bu yüzden Perl'in sembol tablolarını özlüyorum. Javascript'in gelecekteki bir sürümüne eklemeyi planlıyor musunuz?
Dexygen

Yanıtlar:


84

Hayır. "Kapsamda" değişkenler, programlı olarak erişilemeyen "kapsam zinciri" tarafından belirlenir.

Ayrıntılar için (oldukça fazla), ECMAScript (JavaScript) spesifikasyonuna bakın. Burada , kanonik özellikleri (PDF) indirebileceğiniz resmi sayfaya bir bağlantı ve işte resmi, bağlanabilir HTML sürümüne bir bağlantı.

Camsoft hakkındaki yorumunuza göre güncelleme

Olay işlevinizin kapsamındaki değişkenler , olay işlevinizi nasıl adlandırdığınız değil, nerede tanımladığınıza göre belirlenir. Ancak , thisKennyTM'in işaret ettiği ( for (var propName in ____)) satırlarında bir şeyler yaparak işlevinizle nelerin kullanılabilir olduğu ve argümanlar hakkında yararlı bilgiler bulabilirsiniz, çünkü bu size sağlanan çeşitli nesnelerde neyin mevcut olduğunu ( thisve argümanları; size hangi argümanları verdiklerinden emin değilseniz, argumentsher işlev için örtük olarak tanımlanan değişken üzerinden öğrenebilirsiniz ).

Bu nedenle, işlevinizi nerede tanımladığınız nedeniyle kapsamdaki her şeye ek olarak, başka yollarla başka neler kullanılabileceğini de öğrenebilirsiniz:

var n, arg, name;
alert("typeof this = " + typeof this);
for (name in this) {
    alert("this[" + name + "]=" + this[name]);
}
for (n = 0; n < arguments.length; ++n) {
    arg = arguments[n];
    alert("typeof arguments[" + n + "] = " + typeof arg);
    for (name in arg) {
        alert("arguments[" + n + "][" + name + "]=" + arg[name]);
    }
}

(Daha faydalı bilgiler almak için bunu genişletebilirsiniz.)

Bunun yerine, muhtemelen Chrome'un geliştirici araçları (geliştirme için normalde Chrome kullanmasanız bile ) veya Firebug (normalde geliştirme için Firefox kullanmasanız bile) veya Opera'daki Dragonfly gibi bir hata ayıklayıcı kullanırım veya IE'de "F12 Geliştirici Araçları". Ve size sağladıkları JavaScript dosyalarını okuyun. Ve onları doğru dokümanlar için kafasına yendi. :-)


Bir yan not olarak, Google Chrome, Firebug ile karşılaştırılabilir harika bir hata ayıklayıcıya sahiptir (üstte biraz daha fazla buzlanma ile).
döner

4
@Swivelgames: Oh, Chrome'un Geliştirici Araçlarını Firefox + Firebug'a tercih ederim. 2010'da çok fazla geri
dönmediler

1
@tjcrowder Gerçekten! Cevabın üzerindeki zaman damgasının bir süre önce olduğunu fark ettim :)
Swivel

98

Herkes " Hayır " cevabını verse de , "Hayır" ın doğru cevap olduğunu bilsem de, eğer gerçekten bir fonksiyonun yerel değişkenlerini almanız gerekiyorsa sınırlı bir yol vardır.

Bu işlevi göz önünde bulundurun:

var f = function() {
    var x = 0;
    console.log(x);
};

İşlevinizi bir dizeye dönüştürebilirsiniz:

var s = f + '';

Dize olarak işlev kaynağını alırsınız

'function () {\nvar x = 0;\nconsole.log(x);\n}'

Artık işlev kodunu ayrıştırmak ve yerel değişken bildirimlerini bulmak için esprima gibi bir ayrıştırıcı kullanabilirsiniz .

var s = 'function () {\nvar x = 0;\nconsole.log(x);\n}';
s = s.slice(12); // to remove "function () "
var esprima = require('esprima');
var result = esprima.parse(s);

ve aşağıdakileri içeren nesneleri bul:

obj.type == "VariableDeclaration"

sonuçta ( console.log(x)aşağıda kaldırdım ):

{
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "x"
                    },
                    "init": {
                        "type": "Literal",
                        "value": 0,
                        "raw": "0"
                    }
                }
            ],
            "kind": "var"
        }
    ]
}

Bunu Chrome, Firefox ve Node'da test ettim.

Ancak bu yöntemin sorunu , işlevin kendisinde tanımlanan değişkenlere sahip olmanızdır. Örneğin bunun için:

var g = function() {
    var y = 0;
    var f = function() {
        var x = 0;
        console.log(x);
    };
}

y'ye değil , x'e erişebilirsiniz . Ancak , arayan işlevlerinin yerel değişkenlerini bulmak için hala bir döngüde arayan zincirlerini (arguments.callee.caller.caller.caller) kullanabilirsiniz . Tüm yerel değişken adlarına sahipseniz, kapsam değişkenlerine sahip olursunuz . Değişken adları ile basit bir değerlendirme ile değerlere erişebilirsiniz.


7
Burada orijinal sorunun üstesinden gelmek için mükemmel teknik. Bir oyu hak ediyor.
deepelement

4
Arayanın değişkenleri mutlaka kapsam değişkenleri değildir. Ayrıca, kapsam değişkenleri çağrı zincirinde olmak zorunda değildir. Kapatılan değişkenlere, tanım / kapatma kapsamı dışında olan (ve değişkenleri kapanın gövdesine dayanarak erişilemeyen) bir arayandan çağrıldıktan sonra bir kapatma işlevi tarafından başvurulabilir.
DDS

Lütfen "basit bir değerlendirmeyi" açık bir şekilde açıklar mısınız? Aşağıdaki kod ile denedim ama değişken kapsamda sıkışıp alamadım. Ben 2 olması beklenirken function getCounter(start){ return function(){ start ++; return start; } } var counter = getCounter(1); var startInClosure = eval.call(counter, 'this.start;'); console.log(startInClosure);yazdırır undefined.
Pylipala

@Pylipala Buradaki soru, kapsamdaki değişkenlerin, kapsam dışındaki yerel değişkenlerin değerini alamamasıyla ilgilidir. Bu mümkün değil, çünkü bu dışarıdan erişilememesi gereken yerel değişkenin tanımıdır. Kodunuzla ilgili olarak, kapsamı değil bağlamı kastediyorsunuz, ancak bağlamda (bu) başlangıç yapmadınız , bu yüzden bunu this.start ile okuyamazsınız . Buraya bakın: paste.ubuntu.com/11560449
iman

@iman Cevabınız için teşekkürler. Ayrıca Javascript tarafında mümkün olmadığını tahmin, bu yüzden v8 tarafında mümkün olduğunu tahmin ediyorum. Sorunumu doğru bir şekilde açıklayan soru bağlantısını aşağıda buldum . İçinde Lasse Reichstein hayır diyor ama mako-taco evet diyor. Bağlantı olarak bazı C ++ kodu denedim ama nasıl yazılacağını bilmiyorum.
Pylipala

32

Evet ve hayır. Hemen hemen her durumda "Hayır". "Evet", ancak global kapsamı kontrol etmek istiyorsanız, sadece sınırlı bir şekilde. Aşağıdaki örneği alın:

var a = 1, b = 2, c = 3;

for ( var i in window ) {
    console.log(i, typeof window[i], window[i]);
}

Hangi çıktı, 150'den fazla şey arasında aşağıdakiler:

getInterface function getInterface()
i string i // <- there it is!
c number 3
b number 2
a number 1 // <- and another
_firebug object Object firebug=1.4.5 element=div#_firebugConsole
"Firebug command line does not support '$0'"
"Firebug command line does not support '$1'"
_FirebugCommandLine object Object
hasDuplicate boolean false

Bu nedenle, mevcut kapsamdaki bazı değişkenleri listelemek mümkündür, ancak güvenilir, özlü, verimli veya kolayca erişilebilir değildir.

Daha iyi bir soru, neden hangi değişkenlerin kapsamda olduğunu bilmek istediğinizdir?


2
Sorunun daha genel olduğunu ve bir web tarayıcısı bağlamında javascript ile sınırlı olmadığını düşünüyorum.
Radu Simionescu

Düğüm kullanıyorsanız "pencere" kelimesini "global" olarak değiştirin ... veya "bu" kelimesini kullanabilirsiniz, ancak "katı kullanın" altında yasaklanmıştır
Ivan Castellanos

HasOwnProperty için bir satır denetimi ekledim. Örneğin: for ( var i in window ) { if (window.hasOwnProperty(i)) { console.log(i, window[i]); }}. Bu, en azından devralınan özellikleri kesecek ve değişkenleriniz yalnızca yaklaşık 50 mülk arasında olacaktır.
timctran

8
Neden hata ayıklama ve / veya geliştirme amaçları sırasında birileri en azından hangi değişkenlerin kapsamda olduğunu kontrol etmek istemiyor?
Dexygen

Yerel kapsamda hangi değişkenlerin olduğunu bilmek istemenin bir başka nedeni de bir kapatma serileştirmektir.
Michael

29

ECMAScript 6'da, bir withifadenin içindeki kodu bir proxy nesnesiyle sararak az çok mümkündür . Sıkı olmayan mod gerektirdiğini ve kötü uygulama olduğunu unutmayın.

function storeVars(target) {
  return new Proxy(target, {
    has(target, prop) { return true; },
    get(target, prop) { return (prop in target ? target : window)[prop]; }
  });
}
var vars = {}; // Outer variable, not stored.
with(storeVars(vars)) {
  var a = 1;   // Stored in vars
  var b = 2;   // Stored in vars
  (function() {
    var c = 3; // Inner variable, not stored.
  })();
}
console.log(vars);

Proxy with, içinde başvurulan tüm tanımlayıcılara sahip olduğunu iddia ettiğinden , değişken atamaları hedefe depolanır. Aramalarda proxy, proxy hedefinden veya genel nesneden (üst kapsamdan değil) değeri alır. letveconst değişkenler dahil edilmez.

Esinlenerek bu cevap ile Bergi .


1
Bu, böyle basit bir teknik için şaşırtıcı derecede başarılı.
Jonathan Eunice

WOW süper güç
pery mimon

16

Yapamazsın.

Değişkenler, işlev bildirimlerinin tanımlayıcıları ve işlev kodu için bağımsız değişkenler , erişilebilir olmayan Değişken Nesnenin özellikleri olarak bağlanır .

Ayrıca bakınız:


1
Doğru, ancak kapsam içi değişkenler yalnızca geçerli değişken nesnesinin ötesine geçer. Kapsam içi değişkenler, bir dizi değişken nesneden oluşan (normal durumda) ve global nesneyle sonlanan bir zincir olan kapsam zinciri tarafından belirlenir (her ne kadar withdeyim zincire başka nesneler eklemek için de kullanılabilir).
TJ Crowder

Bclary.com bağlantıları için +1. ECMA web sitesinde güncel özelliklerin HTML sürümünün önümüzdeki birkaç ay içinde görüneceğini söylemekten memnuniyet duyuyorum.
TJ Crowder

3
Sadece bir not, her iki bağlantı da bozuk.
0xc0de


8

Belirli Bir Kapsamda Değişkenlere Ulaşmanın En Kolay Yolu

  1. Geliştirici Araçlarını Aç> Kaynaklar (Chrome'da)
  2. Bu kapsama erişimi olan bir işlevi olan dosyayı aç (dosyayı bulmak için cmd / ctrl + p ucu)
  3. Bu işlevin içindeki kesme noktasını ayarlayın ve kodunuzu çalıştırın
  4. Kesme noktanızda durduğunda, kapsam değişkenine konsol (veya kapsam değişken penceresi) üzerinden erişebilirsiniz.

Not: Bunu küçültülmüş j'lere karşı yapmak istersiniz.

Özel Olmayan Tüm Varlıkları Göstermenin En Kolay Yolu

  1. Konsolu Aç (Chrome'da)
  2. Şunu yazın: this.window
  3. Enter tuşuna basın

Şimdi bildirilen tüm nesnelerle genişletebileceğiniz bir nesne ağacı göreceksiniz.


7

Herkesin fark ettiği gibi: yapamazsınız. Ancak bir obj oluşturabilir ve bu objeye beyan ettiğiniz her çeşidi atayabilirsiniz. Bu şekilde varyanlarınızı kolayca kontrol edebilirsiniz:

var v = {}; //put everything here

var f = function(a, b){//do something
}; v.f = f; //make's easy to debug
var a = [1,2,3];
v.a = a;
var x = 'x';
v.x = x;  //so on...

console.log(v); //it's all there

1
+ 1 güzel örnek için teşekkürler, sadece tamamlanması için aynı console.log(window); jsfiddle.net/4x3Tx
Stano

5

Ben yapılan keman iman alan fikirler yukarıdaki (esasen) uygulanması. Fareyi ikinci ipsumun üzerine getirdiğinizdereturn ipsum*ipsum - ...

resim açıklamasını buraya girin

Kapsamdaki değişkenler bildirildikleri yerde vurgulanır (farklı kapsamlar için farklı renklerle). loremKırmızı kenarlıklı gölgede kalan değişkendir (kapsamında, ancak kapsamı içinde olmak daha da ağacın aşağı diğer lorem olmaz değilse.)

JavaScript ve estraverse, escodegen, escope (esprima üstünde yarar kitaplıkları) ayrıştırmak için esprima kütüphanesi kullanıyorum 'Ağır kaldırma' bu kütüphaneler tarafından yapılır (en karmaşık esprima kendisi, tabii ki).

Nasıl çalışır

ast = esprima.parse(sourceString, {range: true, sourceType: 'script'});

soyut sözdizimi ağacını yapar. Sonra,

analysis = escope.analyze(ast);

programdaki tüm kapsamlar hakkında bilgi içeren karmaşık bir veri yapısı oluşturur. Geri kalanı, o analiz nesnesinde (ve soyut sözdizimi ağacının kendisinde) kodlanan bilgileri bir araya getirerek, etkileşimli bir renklendirme şeması oluşturuyor.

Yani doğru cevap aslında "hayır" değil, "evet" değil. "Ama" büyük olanıdır: temel olarak JavaScript'te krom tarayıcısının (ve cihazlarının) önemli bölümlerini yeniden yazmanız gerekir. JavaScript, bir Turing tam dilidir, bu yüzden elbette mümkün. İmkansız olan şey, her şeyi kaynak kodunuzun tamamını (dize olarak) kullanmadan yapmak ve daha sonra bununla son derece karmaşık şeyler yapmaktır.


3

Hata ayıklamaya yardımcı olması için değişkenleri manuel olarak incelemek istiyorsanız, hata ayıklayıcıyı çalıştırmanız yeterlidir:

debugger;

Doğrudan tarayıcı konsoluna.

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.