Получение всех переменных в области видимости

194

Есть ли способ получить все переменные, которые в настоящее время находятся в области видимости в JavaScript?

Ян
источник
Ваш ответ Camsoft: это совершенно другой вопрос; Я обновил свой ответ, чтобы исправить это.
TJ Crowder
3
Это просто общий вопрос, более конкретная помощь не сильно поможет, так как я работаю с непонятным API с плохой документацией.
Ян
Вы имеете в виду глобальные переменные! Вы можете отобразить перечисляемые глобальные переменные, используя for (v in this) alert(v);. Однако не все глобальные перечислимы, и я не знаю ни одного стандартного способа получить список не перечислимых.
Джейсон Орендорфф
2
@ Джейсон - Нет, вопрос ясен. Внутри функции переменные в сферу будет включать в себя глобальные переменные, this, arguments, параметры и все переменные , определенные в ограждающих областей.
Тим Даун
2
Вот почему я скучаю по таблицам символов Perl. Есть ли планы добавить это в будущий выпуск Javascript?
Кислород

Ответы:

84

Нет. Переменные "в области видимости" определяются "цепочкой областей действия", которая недоступна программно.

Для деталей (довольно много), проверьте спецификацию ECMAScript (JavaScript). Вот ссылка на официальную страницу , где вы можете скачать каноническую спецификацию (а) PDF, и вот один к официальной, вулканизующемуся HTML версии.

Обновление на основе вашего комментария к Camsoft

Переменные в области видимости вашей функции события определяются тем, где вы определяете свою функцию события, а не тем, как они ее вызывают. Но вы можете найти полезную информацию о том, что доступно для вашей функции через thisаргументы и аргументы, выполнив что-то в соответствии с тем, на что указал KennyTM ( for (var propName in ____)), так как это скажет вам, что доступно для различных предоставленных вам объектов ( thisи аргументов; если вы не уверен, какие аргументы они вам дают, вы можете узнать через argumentsпеременную, которая неявно определена для каждой функции).

Таким образом, в дополнение к тому, что находится в области действия, из-за того, где вы определяете свою функцию, вы можете узнать, что еще доступно другими способами, выполнив:

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

(Вы можете расширить это, чтобы получить больше полезной информации.)

Вместо этого, однако, я бы, вероятно, использовал отладчик, такой как инструменты разработчика Chrome (даже если вы обычно не используете Chrome для разработки) или Firebug (даже если вы обычно не используете Firefox для разработки), или Dragonfly в Opera или «Инструменты разработчика F12» в IE. И прочитайте все файлы JavaScript, которые они вам предоставляют. И бить их по голове для правильных документов. :-)

TJ Crowder
источник
Как примечание, у Google Chrome есть отличный отладчик, сравнимый с Firebug (с немного большим количеством обледенения также).
Поворот
4
@Swivelgames: О, я предпочитаю инструменты разработчика Chrome Firefox + Firebug. В 2010 году их было немного. :-)
TJ Crowder
1
@tjcrowder Действительно! Я заметил, что отметка времени ответа была некоторое время назад :)
Swivel
98

Хотя все отвечают « Нет », и я знаю, что «Нет» - правильный ответ, но если вам действительно нужно получить локальные переменные функции, есть ограниченный путь.

Рассмотрим эту функцию:

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

Вы можете преобразовать свою функцию в строку:

var s = f + '';

Вы получите источник функции в виде строки

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

Теперь вы можете использовать парсер наподобие esprima для разбора кода функции и поиска объявлений локальных переменных.

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

и найти объекты с:

obj.type == "VariableDeclaration"

в результате (я удалил console.log(x)ниже):

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

Я проверил это в Chrome, Firefox и Node.

Но проблема этого метода в том, что у вас просто есть переменные, определенные в самой функции. Например, для этого:

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

у вас просто есть доступ к х, а не к у . Но все же вы можете использовать цепочки вызывающего (arguments.callee.caller.caller.caller) в цикле, чтобы найти локальные переменные вызывающих функций. Если у вас есть все локальные имена переменных, значит, у вас есть переменные области видимости . С именами переменных у вас есть доступ к значениям с простым Eval.

иман
источник
7
Отличная техника здесь при решении исходной проблемы. Заслуживает голоса.
углубление
4
Переменные вызывающей стороны не обязательно являются переменными области видимости. Кроме того, переменные области видимости не обязательно находятся в цепочке вызовов. Переменные, которые были закрыты, могут ссылаться на функцию замыкания при вызове из вызывающей стороны, которая находится вне области определения / замыкания (и чьи переменные вообще недоступны из-за тела замыкания).
DDS
Не могли бы вы объяснить "простой eval" ясно? Я попытался с кодом ниже, но не смог получить переменную в ловушке в области видимости. function getCounter(start){ return function(){ start ++; return start; } } var counter = getCounter(1); var startInClosure = eval.call(counter, 'this.start;'); console.log(startInClosure);Он печатается в undefinedто время как я ожидаю, что это должно быть 2.
Pylipala
@Pylipala Вопрос здесь заключается в том, что переменные в области видимости не получают значения локальных переменных вне области видимости. Это невозможно, потому что это определение локальной переменной, которая не должна быть доступна извне. Что касается вашего кода, вы имеете в виду контекст, а не область, но вы не поместили начало в контекст (это), поэтому вы не можете прочитать его с помощью этого .start . Смотрите здесь: paste.ubuntu.com/11560449
иман
@iman Спасибо за ваш ответ. Я также думаю, что это невозможно на стороне Javascript, поэтому я думаю, что это возможно на стороне v8. Ниже я нашел ссылку на вопрос, которая точно описывает мою проблему. В ней Лассе Рейхштейн говорит «нет», а мако-тако говорит «да». Я попробовал некоторый код C ++ как ссылку, но не знаю, как его написать.
Pylipala
32

Да и нет. «Нет» почти в каждой ситуации. «Да», но только ограниченным образом, если вы хотите проверить глобальную область. Возьмите следующий пример:

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

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

Какие выходы, среди 150+ других вещей , следующие:

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

Таким образом, можно перечислить некоторые переменные в текущей области видимости, но они не являются надежными, краткими, эффективными или легко доступными.

Лучший вопрос: почему вы хотите знать, какие переменные находятся в области видимости?

Джастин Джонсон
источник
2
Я думаю, что вопрос был более общим и не ограничивался javascript в контексте веб-браузера.
Раду Симионеску
Просто измените слово «окно» на «глобальный», если вы используете нод ... или вы можете использовать слово «этот», но это запрещено в разделе «использовать строгий»
Иван Кастелланос
Я добавил проверку строки для hasOwnProperty. Например: for ( var i in window ) { if (window.hasOwnProperty(i)) { console.log(i, window[i]); }}. Это, по крайней мере, сократит унаследованные свойства, и ваши переменные будут среди только около 50 других свойств.
тимктран
8
Почему бы кому-то не захотеть хотя бы проверить, какие переменные находятся в области видимости, во время отладки и / или разработки.
Кислород
Другая причина узнать, какие переменные находятся в локальной области видимости - это сериализация замыкания.
Майкл
29

В ECMAScript 6 это более или менее возможно, если обернуть код внутри withоператора прокси-объектом. Обратите внимание, что требуется не строгий режим и это плохая практика.

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

Прокси-сервер утверждает, что владеет всеми идентификаторами, на которые есть ссылки with, поэтому назначения переменных сохраняются в целевом объекте. Для поиска прокси получает значение из цели прокси или глобального объекта (не родительской области). letи constпеременные не включены.

Вдохновленный этим ответом по Берги .

Ориоль
источник
1
Это удивительно успешно для такой простой техники.
Джонатан Юнис
WOW, супер сила
Pery Mimon
16

Ты не можешь

Переменные, идентификаторы объявлений функций и аргументы для кода функции, связаны как свойства объекта Variable , который недоступен.

Смотрите также:

CMS
источник
1
Верно, но переменные в области видимости выходят за рамки только текущего объекта переменной. Переменные в области видимости определяются цепью области видимости , которая представляет собой цепочку, состоящую (в обычном случае) из серии переменных объектов и оканчивающуюся глобальным объектом (хотя withоператор может использоваться для вставки других объектов в цепочку).
TJ Crowder
+1 за ссылки на bclary.com. Я рад сообщить, что HTML-версия обновленной спецификации появится в ближайшие пару месяцев на сайте ECMA.
TJ Crowder
3
Просто примечание, обе ссылки не работают.
0xc0de
Вы МОЖЕТЕ - со статическим анализом jsfiddle.net/mathheadinclouds/bvx1hpfn/11
mathheadinclouds
8

Самый простой способ получить доступ к Vars в определенном объеме

  1. Откройте Инструменты разработчика> Ресурсы (в Chrome)
  2. Откройте файл с помощью функции, которая имеет доступ к этой области (подсказка cmd / ctrl + p, чтобы найти файл)
  3. Установите точку останова внутри этой функции и запустите ваш код
  4. Когда он останавливается на вашей точке останова, вы можете получить доступ к области видимости через консоль (или окно области видимости)

Примечание: вы хотите сделать это против неунифицированного JS.

Самый простой способ показать все не частные переменные

  1. Открытая консоль (в Chrome)
  2. Тип: это окно
  3. Нажмите Enter

Теперь вы увидите дерево объектов, которое вы можете расширить всеми заявленными объектами.

Тимоти Перес
источник
7

Как все заметили: нельзя. Но вы можете создать объект и назначить каждый объявленный вами объект этому объекту. Таким образом, вы можете легко проверить свои переменные:

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
источник
1
+1 спасибо за прохладного , например, только для завершения то же самое можно увидеть и через console.log(window); jsfiddle.net/4x3Tx
Stano
5

Я сделал скрипку, реализуя (по существу) выше идеи, изложенные в Iman. Вот как это выглядит, когда вы наводите курсор мыши на второй ipsum вreturn ipsum*ipsum - ...

введите описание изображения здесь

Переменные, которые находятся в области видимости, подсвечиваются там, где они объявлены (разными цветами для разных областей). loremС красной границы затененной переменной (не входят в комплект, но находиться в области видимости , если другой Lorem далее вниз по дереву бы там не будет.)

Я использую библиотеку esprima для синтаксического анализа JavaScript и estraverse, escodegen, escope (служебные библиотеки поверх esprima). «Тяжелая работа» выполняется всеми этими библиотеками (конечно, самой сложной является сама esprima).

Как это устроено

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

создает абстрактное синтаксическое дерево. Затем,

analysis = escope.analyze(ast);

генерирует сложную структуру данных, инкапсулирующую информацию обо всех областях действия программы. Остальное собирает вместе информацию, закодированную в этом объекте анализа (и само абстрактное синтаксическое дерево), и создает из него интерактивную схему раскраски.

Таким образом, правильный ответ на самом деле не «нет», а «да, но». «Но» важно: вам нужно переписать важные части браузера Chrome (и его devtools) на JavaScript. JavaScript - это полный язык Тьюринга, поэтому, конечно, это возможно в принципе. Невозможно сделать все это без использования всего исходного кода (в виде строки), а затем выполнить очень сложные вещи с этим.

mathheadinclouds
источник
3

Если вы просто хотите проверить переменные вручную, чтобы помочь отладке, просто запустите отладчик:

debugger;

Прямо в консоль браузера.

Дэвид Майстер
источник