Отримання всіх змінних в масштабі


194

Чи є спосіб отримати всі змінні, які зараз є в області JavaScript?


Повторіть свою відповідь Camsoft: Це зовсім інше питання; Я оновив свою відповідь, щоб вирішити її.
TJ Crowder

3
Це просто загальне питання: більш конкретна допомога не допоможе, оскільки я працюю з неясним API з поганою документацією.
Ян

Ви маєте на увазі глобальні змінні! Ви можете відобразити безліч глобальних змінних, використовуючи for (v in this) alert(v);. Однак, не всі глобальні переліки є численними, і я не знаю жодного стандартного способу отримати список тих, хто не перелічує.
Джейсон Орендорф

2
@Jason - Ні, питання зрозуміле. Усередині функції змінні в сферу включатиме в себе глобальні змінні, this, arguments, параметри і всі змінні , визначені в огороджувальних областей.
Тім Даун

2
Ось чому я сумую за таблицями символів Перла. Будь-які плани додати це до майбутнього випуску Javascript?
Dexygen

Відповіді:


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, які вони вам надають. І бити їх по голові за належні документи. :-)


В якості додаткового зауваження, Google Chrome має чудовий налагоджувач, порівнянний з Firebug (з трохи більше обмерзання на вершині).
Поворот

4
@Swivelgames: О, я більше віддаю перевагу Dev Tools Chrome для Firefox + Firebug. Їх просто не було дуже багато в 2010 році :-)
TJ Crowder

1
@tjcrowder Дійсно! Я помітив часову позначку у відповіді, яка була давна :)
Поворот

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

у вас просто доступ до x, а не y . Але все ж ви можете використовувати ланцюги виклику (argument.callee.caller.caller.caller) у циклі, щоб знайти локальні змінні функції виклику. Якщо у вас є всі локальні імена змінних, значить, у вас є змінні області . Імена змінних у вас є доступ до значень з простим eval.


7
Відмінна техніка тут для вирішення початкової проблеми. Заслуговує на підсумок.
заглиблення

4
Змінні абонента, які викликають абонент, не обов'язково є змінними. Крім того, змінні області не обов'язково в ланцюзі викликів. Змінні, які були закриті, можуть посилатися на функцію закриття, викликаючи його від абонента, який знаходиться поза межами області визначення / закриття (і змінні яких взагалі не доступні через тестування закриття).
DDS

Чи можете ви поясніть, будь ласка, зрозумілий "простий евал"? Я намагався з кодом нижче, але не зміг потрапити в змінну область, що потрапила в область. 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 Тут йдеться про те, що змінні за обсягом не набувають значення локальних змінних за межами області. Це неможливо, оскільки це визначення локальної змінної, яка не повинна бути доступною ззовні. Що стосується вашого коду, ви маєте на увазі контекст не сферу дії, але ви не ставили старт у контексті (це), тому ви не можете прочитати його з цим.start . Дивіться тут: paste.ubuntu.com/11560449
іман

@iman Дякую за вашу відповідь. Я також здогадуюсь, що це не можливо в стороні Javascript, тому я здогадуюсь, чи це можливо на стороні v8. Нижче я знайшов посилання питань, яке точно описує мою проблему. У цьому Лассе Райхштайн говорить ні, але мако-тако говорить так. Я спробував якийсь код C ++ як посилання, але не знаю, як його написати.
Пилипала

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 інших властивостей.
timctran

8
Чому хтось хоча б не хоче перевіряти, які змінні знаходяться в області застосування, під час налагодження та / або цілей розробки.
Dexygen

Ще одна причина, щоб хотіти знати, які змінні знаходяться в локальному масштабі, - це серіалізація закриття.
Майкл

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 super power
pery mimon

16

Ви не можете.

Змінні, ідентифікатори оголошень функції та аргументи для коду функції, пов'язані як властивості об'єкта змінної , який не є доступним.

Дивитися також:


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. Коли він зупиниться у вашій точці розриву, ви можете отримати доступ до змінної області через консоль (або вікно var var)

Примітка. Ви хочете зробити це проти неміфікованих js.

Найпростіший спосіб показати всі не приватні Vars

  1. Відкрити консоль (у Chrome)
  2. Введіть: this.window
  3. Натисніть Enter

Тепер ви побачите дерево об'єктів, яке можна розгорнути всіма заявленими об'єктами.


7

Як всі помітили: не можна. Але ви можете створити obj і призначити кожен var, який ви заявляєте, для цього obj. Таким чином ви зможете легко перевірити свої послуги:

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

5

Я склав загадку, реалізуючи (по суті) вище ідеї, викладені імманом. Ось як це виглядає, коли ви кладете курсор миші на другий ipsum дюймаreturn ipsum*ipsum - ...

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

Змінні, що знаходяться в області застосування, виділяються там, де вони оголошені (різними кольорами для різних областей). loremЗ червоною кордону затіненій змінної (не входять в комплект, але перебувати в області видимості , якщо інший Lorem далі вниз по дереву б там не буде.)

Я використовую бібліотеку esprima для розбору JavaScript, а estraverse, escodegen, escope (корисні бібліотеки поверх esprima.) "Важкий підйом" роблять усі ці бібліотеки (звичайно, найскладніша сама есприма).

Як це працює

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

робить абстрактне синтаксичне дерево. Тоді,

analysis = escope.analyze(ast);

генерує складну структуру даних, що інкапсулює інформацію про всі сфери дії програми. Решта збирає разом інформацію, закодовану в цьому об’єкті аналізу (і саме абстрактне синтаксичне дерево), і створює з нього інтерактивну схему забарвлення.

Тож правильна відповідь насправді не "ні", а "так, але". Будучи великим "але": вам в основному потрібно переписати значні частини хромованого браузера (і це devtools) в JavaScript. JavaScript є повною мовою Тюрінга, тому, звичайно, це можливо в принципі. Що неможливо - це робити все, не використовуючи весь вихідний код (як рядок), а потім робити дуже складні речі з цим.


3

Якщо ви просто хочете перевірити змінні вручну, щоб допомогти налагодити, просто запустіть налагоджувач:

debugger;

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.