Mendapatkan Semua Variabel Dalam Lingkup

194

Apakah ada cara untuk mendapatkan semua variabel yang saat ini dalam cakupan di javascript?

Ian
sumber
Berikan jawaban Anda kepada Camsoft: Itu pertanyaan yang sama sekali berbeda; Saya telah memperbarui jawaban saya untuk mengatasinya.
TJ Crowder
3
Ini hanya pertanyaan umum, lebih spesifik tidak akan banyak membantu karena saya bekerja dengan API tidak jelas dengan dokumentasi yang buruk.
Ian
Maksud Anda variabel global! Anda dapat menampilkan variabel global enumerable menggunakan for (v in this) alert(v);. Tidak semua global bisa dihitung, dan saya tahu tidak ada cara standar untuk mendapatkan daftar yang tidak dapat dihitung.
Jason Orendorff
2
@Jason - Tidak, pertanyaannya jelas. Dalam fungsi variabel dalam lingkup akan mencakup variabel global, this, arguments, parameter dan semua variabel yang didefinisikan dalam melampirkan lingkup.
Tim Down
2
Inilah sebabnya saya kehilangan tabel simbol Perl. Adakah rencana untuk menambahkan ini ke rilis Javascript yang akan datang?
Dexygen

Jawaban:

84

Tidak. Variabel "Dalam lingkup" ditentukan oleh "rantai lingkup", yang tidak dapat diakses secara programatis.

Untuk detail (cukup banyak), lihat spesifikasi ECMAScript (JavaScript). Berikut ini tautan ke halaman resmi tempat Anda dapat mengunduh spesifikasi kanonik (PDF), dan ini satu ke versi HTML resmi yang dapat ditautkan.

Perbarui berdasarkan komentar Anda ke Camsoft

Variabel dalam ruang lingkup untuk fungsi acara Anda ditentukan oleh tempat Anda menentukan fungsi acara, bukan bagaimana mereka menyebutnya. Tapi , Anda dapat menemukan informasi berguna tentang apa yang tersedia untuk fungsi Anda melalui thisdan argumen dengan melakukan sesuatu di sepanjang garis yang ditunjukkan KennyTM ( for (var propName in ____)) karena itu akan memberi tahu Anda apa yang tersedia pada berbagai objek yang disediakan untuk Anda ( thisdan argumen; jika Anda tidak yakin argumen apa yang mereka berikan kepada Anda, Anda dapat mencari tahu melalui argumentsvariabel yang didefinisikan secara implisit untuk setiap fungsi).

Jadi selain apa pun yang ada di ruang lingkup karena di mana Anda mendefinisikan fungsi Anda, Anda dapat mengetahui apa lagi yang tersedia dengan cara lain dengan melakukan:

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

(Anda dapat mengembangkannya untuk mendapatkan informasi yang lebih bermanfaat.)

Alih-alih, saya mungkin menggunakan debugger seperti alat dev Chrome (bahkan jika Anda biasanya tidak menggunakan Chrome untuk pengembangan) atau Firebug (bahkan jika Anda biasanya tidak menggunakan Firefox untuk pengembangan), atau Dragonfly on Opera , atau "Alat Pengembang F12" di IE. Dan baca semua file JavaScript yang mereka berikan kepada Anda. Dan mengalahkan mereka di atas kepala untuk dokumen yang tepat. :-)

TJ Crowder
sumber
Sebagai catatan, Google Chrome memiliki debugger hebat yang sebanding dengan Firebug (dengan sedikit lebih banyak lapisan icing di atasnya).
Putar
4
@Swivelgames: Oh, saya lebih suka Alat Dev Chrome untuk Firefox + Firebug. Mereka hanya tidak banyak pada tahun 2010. :-)
TJ Crowder
1
@tjcrowder Memang! Saya perhatikan cap waktu pada jawaban itu beberapa waktu yang lalu :)
Swivel
98

Meskipun semua orang menjawab " Tidak " dan saya tahu bahwa "Tidak" adalah jawaban yang tepat tetapi jika Anda benar-benar perlu mendapatkan variabel lokal dari suatu fungsi ada cara terbatas.

Pertimbangkan fungsi ini:

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

Anda dapat mengubah fungsi Anda menjadi string:

var s = f + '';

Anda akan mendapatkan sumber fungsi sebagai string

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

Sekarang Anda dapat menggunakan parser seperti esprima untuk mengurai kode fungsi dan menemukan deklarasi variabel lokal.

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

dan temukan benda dengan:

obj.type == "VariableDeclaration"

dalam hasilnya (saya telah menghapus di console.log(x)bawah):

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

Saya telah menguji ini di Chrome, Firefox dan Node.

Tetapi masalah dengan metode ini adalah Anda hanya memiliki variabel yang didefinisikan dalam fungsi itu sendiri. Misalnya untuk yang ini:

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

Anda hanya memiliki akses ke x dan bukan y . Tetapi Anda masih dapat menggunakan rantai pemanggil (arguments.callee.caller.caller.caller) dalam satu lingkaran untuk menemukan variabel lokal fungsi pemanggil. Jika Anda memiliki semua nama variabel lokal, maka Anda memiliki variabel lingkup . Dengan nama variabel Anda memiliki akses ke nilai dengan eval sederhana.

iman
sumber
7
Teknik yang sangat baik di sini untuk mengatasi masalah aslinya. Pantas mendapat dukungan.
deepelement
4
Variabel penelepon tidak harus variabel lingkup. Selain itu, variabel lingkup tidak harus dalam rantai panggilan. Variabel yang telah ditutup mungkin direferensikan oleh fungsi penutupan setelah memanggilnya dari pemanggil yang berada di luar ruang lingkup definisi / penutupan (dan yang variabelnya sama sekali tidak dapat diakses dari dengan membawa tubuh penutupan).
DDS
Bisakah Anda jelaskan "eval sederhana" jelas? Saya mencoba dengan kode di bawah ini tetapi tidak bisa mendapatkan variabel terperangkap dalam ruang lingkup. function getCounter(start){ return function(){ start ++; return start; } } var counter = getCounter(1); var startInClosure = eval.call(counter, 'this.start;'); console.log(startInClosure);Itu mencetak undefinedsementara saya berharap itu harus 2.
Pylipala
@ Pilipala Pertanyaannya di sini adalah tentang variabel dalam lingkup tidak mendapatkan nilai dari variabel lokal dari luar ruang lingkup. Itu tidak mungkin, karena ini adalah definisi dari variabel lokal yang tidak boleh diakses dari luar. Mengenai kode Anda, maksud Anda konteks bukan cakupan tetapi Anda tidak menempatkan awal dalam konteks (ini), jadi Anda tidak dapat membacanya dengan this.start . Lihat di sini: paste.ubuntu.com/11560449
iman
@iman Terima kasih atas jawaban Anda. Saya juga kira itu tidak mungkin di sisi Javascript, jadi saya kira jika mungkin di sisi v8. Saya menemukan tautan pertanyaan di bawah ini yang menjelaskan masalah saya secara akurat. Di dalamnya Lasse Reichstein mengatakan tidak, tetapi mako-taco mengatakan ya. Saya mencoba beberapa kode C ++ sebagai tautan tetapi tidak tahu bagaimana menulisnya.
Pylipala
32

Iya dan tidak. "Tidak" di hampir setiap situasi. "Ya," tetapi hanya secara terbatas, jika Anda ingin memeriksa ruang lingkup global. Ambil contoh berikut:

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

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

Output mana, di antara 150+ hal lainnya , berikut ini:

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

Jadi dimungkinkan untuk membuat daftar beberapa variabel dalam lingkup saat ini, tetapi tidak dapat diandalkan, ringkas, efisien, atau mudah diakses.

Pertanyaan yang lebih baik adalah mengapa Anda ingin tahu variabel apa yang ada dalam ruang lingkup?

Justin Johnson
sumber
2
Saya pikir pertanyaannya lebih umum dan tidak terbatas pada javascript dalam konteks browser web.
Radu Simionescu
Cukup ubah kata "jendela" untuk "global" jika Anda menggunakan simpul ... atau Anda dapat menggunakan kata "ini" tetapi itu dilarang di bawah "gunakan ketat"
Ivan Castellanos
Saya menambahkan garis memeriksa hasOwnProperty. Sebagai contoh: for ( var i in window ) { if (window.hasOwnProperty(i)) { console.log(i, window[i]); }}. Ini setidaknya akan mengurangi properti yang diwarisi dan variabel Anda hanya akan berada di antara sekitar 50 properti lainnya.
timctran
8
Mengapa seseorang tidak setidaknya ingin memeriksa variabel apa yang ada dalam ruang lingkup, selama debugging dan / atau tujuan pengembangan.
Dexygen
Alasan lain untuk ingin tahu variabel apa yang ada dalam lingkup lokal adalah untuk membuat serial penutupan.
Michael
29

Dalam ECMAScript 6 lebih atau kurang mungkin dengan membungkus kode di dalam withpernyataan dengan objek proxy. Catatan itu membutuhkan mode non-ketat dan itu praktik yang buruk.

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

Proksi mengklaim memiliki semua pengidentifikasi yang dirujuk di dalam with, sehingga tugas variabel disimpan dalam target. Untuk pencarian, proksi mengambil nilai dari target proksi atau objek global (bukan lingkup induk). letdan constvariabel tidak termasuk.

Terinspirasi oleh jawaban ini oleh Bergi .

Oriol
sumber
1
Ini secara mengejutkan berhasil untuk teknik yang begitu sederhana.
Jonathan Eunice
WOW kekuatan super
pery mimon
16

Kamu tidak bisa

Variabel, pengidentifikasi deklarasi fungsi dan argumen untuk kode fungsi, terikat sebagai properti dari Objek Variabel , yang tidak dapat diakses.

Lihat juga:

CMS
sumber
1
Benar, tetapi variabel dalam lingkup melampaui hanya objek variabel saat ini. Variabel dalam lingkup ditentukan oleh rantai lingkup , yang merupakan rantai yang terdiri (dalam kasus normal) dari serangkaian objek variabel dan diakhiri dengan objek global (meskipun withpernyataan dapat digunakan untuk memasukkan objek lain dalam rantai).
TJ Crowder
+1 untuk tautan ke bclary.com. Versi HTML dari spesifikasi terbaru akan muncul dalam beberapa bulan ke depan di situs web ECMA, saya senang mengatakannya.
TJ Crowder
3
Sekedar catatan, kedua tautannya terputus.
0xc0de
Anda BISA - dengan analisis statis jsfiddle.net/mathheadinclouds/bvx1hpfn/11
mathheadinclouds
8

Cara Termudah untuk Mendapatkan Akses ke Vars dalam Lingkup Tertentu

  1. Buka Alat Pengembang> Sumber Daya (di Chrome)
  2. Buka file dengan fungsi yang memiliki akses ke lingkup itu (tip cmd / ctrl + p untuk menemukan file)
  3. Atur breakpoint di dalam fungsi itu dan jalankan kode Anda
  4. Ketika berhenti di breakpoint Anda, Anda dapat mengakses lingkup var melalui konsol (atau jendela lingkup var)

Catatan: Anda ingin melakukan ini terhadap js yang tidak dicincang.

Cara Termudah untuk Menampilkan Semua Vars Non-Pribadi

  1. Buka Konsol (di Chrome)
  2. Ketik: this.window
  3. Tekan Enter

Sekarang Anda akan melihat pohon objek yang dapat Anda kembangkan dengan semua objek yang dideklarasikan.

Timothy Perez
sumber
7

Seperti yang semua orang perhatikan: Anda tidak bisa. Tetapi Anda dapat membuat obj dan menetapkan setiap var yang Anda nyatakan untuk obj itu. Dengan begitu Anda dapat dengan mudah memeriksa vars Anda:

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
rafaelcastrocouto
sumber
1
+1 terima kasih untuk contoh keren, hanya untuk penyelesaian yang sama dapat dilihat juga melalui console.log(window); jsfiddle.net/4x3Tx
Stano
5

Saya membuat biola menerapkan (pada dasarnya) ide-ide di atas yang diuraikan oleh iman. Berikut ini tampilannya ketika Anda mengarahkan mouse ke ipsum keduareturn ipsum*ipsum - ...

masukkan deskripsi gambar di sini

Variabel yang berada dalam ruang lingkup disorot di mana mereka dideklarasikan (dengan warna berbeda untuk cakupan berbeda). Itulorem bertepi merah adalah variabel Dibayangi (tidak dalam ruang lingkup, tetapi dalam lingkup jika lorem lainnya lebih bawah pohon tidak akan ada.)

Saya menggunakan perpustakaan esprima untuk mem-parsing JavaScript, dan estraverse, escodegen, escope (perpustakaan utilitas di atas esprima.) 'Pengangkatan berat' dilakukan semua oleh perpustakaan-perpustakaan itu (tentu saja yang paling kompleks adalah esprima itu sendiri.)

Bagaimana itu bekerja

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

membuat pohon sintaksis abstrak. Kemudian,

analysis = escope.analyze(ast);

menghasilkan struktur data yang kompleks merangkum informasi tentang semua ruang lingkup dalam program. Sisanya mengumpulkan informasi yang disandikan dalam objek analisis (dan pohon sintaksis abstrak itu sendiri), dan membuat skema pewarnaan interaktif darinya.

Jadi jawaban yang benar sebenarnya bukan "tidak", tapi "ya, tapi". "Tapi" yang besar: Anda pada dasarnya harus menulis ulang bagian penting dari browser chrome (dan itu devtools) di JavaScript. JavaScript adalah bahasa lengkap Turing, jadi tentu saja itu mungkin, pada prinsipnya. Yang tidak mungkin adalah melakukan semuanya tanpa menggunakan keseluruhan kode sumber Anda (sebagai string) dan kemudian melakukan hal-hal yang sangat kompleks dengan itu.

matematika seperti awan
sumber
3

Jika Anda hanya ingin memeriksa variabel secara manual untuk membantu debug, jalankan saja debugger:

debugger;

Langsung ke konsol browser.

David Meister
sumber