1 (изменено: jite, 2011-04-19 01:56:08)

Тема: JScript: Вывод свойств и значений массива или объекта - дополнение

Upd. Последнюю версию, довольно далеко ушедшую от этой, смотрим в последних постах данной темы

Хочу поделиться вариантом в дополнение к теме http://forum.script-coding.com/viewtopic.php?id=3961
Версия с WScript.Echo(). НЕ рекомендуется использовать с wscript.exe!

//Рекурсивное описание содержимого объекта obj через WScript.Echo()
//obj - исследуемый объект
//padding - начальный отступ, в padding_char единицах (padding_char="  ")
//parent_name - имя объекта (произвольное, по сути комментарий)
//Использование: vardump(myObj, 2, "Объект №5");
//Можно проще: vardump(myObj);
function vardump(obj, padding, parent_name)
{
  if (typeof(padding) == "undefined") //Определяем, если не определено (опущено при вызове)
    padding = 0;  //Отступ
  if (typeof(parent_name) == "undefined") 
    parent_name = ""; //Имя родителя
  var no_space_for_first = (parent_name == "") ? "" : " "; //Чтобы у корневого (безымянного) объекта не было пробела перед типом
  var padding_char = "  ";
  var pre_string = "";
  for (var i=0; i<padding; i++) //Набираем отступ
    pre_string += padding_char;

  WScript.Echo(pre_string + parent_name + no_space_for_first +"(" + typeof(obj) + ")=>" + obj + "<"); //Выводим имя (тип)=>значение<
  if (typeof(obj) == "object")
  {
    for (var child in obj)
    {
      var child_name = parent_name; //Делаем имя потомку
      if (child_name != "")
        child_name += ".";
      child_name += child; 
      arguments.callee(obj[child], padding+1, child_name);
    }
  }
} //vardump

Универсальный вариант, возвращающий все одной string

//Возвращает string: рекурсивное описание содержимого объекта obj
function svd(obj, padding, parent_name)
{
  if (typeof(padding) == "undefined") //Определяем, если не определено (опущено при вызове)
    padding = 0;  //Отступ
  if (typeof(parent_name) == "undefined") 
    parent_name = ""; //Имя родителя
  var no_space_for_first = (parent_name == "") ? "" : " "; //Чтобы у корневого (безымянного) объекта не было пробела перед типом
  var padding_char = "  ";
  var pre_string = "";
  for (var i=0; i<padding; i++) //Набираем отступ
    pre_string += padding_char;

  var result = pre_string + parent_name + no_space_for_first +"(" + typeof(obj) + ")=>" + obj + "<\n"; //Выводим имя (тип)=>значение<
  if (typeof(obj) == "object")
    for (var child in obj)
    {
      var child_name = parent_name; //Делаем имя потомку
      if (child_name != "")
        child_name += ".";
      child_name += child; 
      result += arguments.callee(obj[child], padding+1, child_name);
    }
  return(result);
} //svd

Отличия:
* выделение степени вложенности
* указание типов
* указание границ значений (значками ">" и "<"), кому не надо - стереть не составит труда
Упомянутый в источнике объект vardump(myArr) выводит следующим образом:

(object)=>string,function(){return a+b},[object Object],98,str,function(){x++},[object Object]<
  0 (string)=>string<
  1 (function)=>function(){return a+b}<
  2 (object)=>[object Object]<
    2.a (number)=>32<
    2.b (string)=>v4<
    2.c (object)=>1,a<
      2.c.0 (number)=>1<
      2.c.1 (string)=>a<
  3 (object)=>98,str,function(){x++},[object Object]<
    3.0 (number)=>98<
    3.1 (string)=>str<
    3.2 (function)=>function(){x++}<
    3.3 (object)=>[object Object]<
      3.3.prop (string)=>value<

для сравнения, вывод оригинала:

['0'] = string
['1'] = function(){return a+b}
['2']['a'] = 32
['2']['b'] = v4
['2']['c']['0'] = 1
['2']['c']['1'] = a
['3']['0'] = 98
['3']['1'] = str
['3']['2'] = function(){x++}
['3']['3']['prop'] = value

2

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

jite пишет:

...
Версия с WScript.Echo(). НЕ рекомендуется использовать с wscript.exe! :)

//Рекурсивное описание содержимого объекта obj через WScript.Echo()
...

Что мешает обработать объект вначале и вернуть строку, с которой можно уже делать что угодно?

Попробуйте посмотреть содержимое такого объекта

var object = {};
object.key = object;
( 2 * b ) || ! ( 2 * b )

3 (изменено: jite, 2010-04-20 18:09:52)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Что мешает обработать объект вначале и вернуть строку, с которой можно уже делать что угодно?

Намекаете, что описанный мною в том же посте вариант svd(), возвращающий string, надо было поставить первым номером? Просто при отладке сценариев для WSH vardump(obj) писать удобней чем WScript.Echo(vardump(obj)).
Хотя с недавнего времени осознал, что все-таки WScript.Echo() неудобно: wscript задалбывает ОК-окошками, а для cscript надо отдельно консоль открывать, чтобы узнать, чего он там понаписал. Собсно поэтому и появился svd() - ... и в лог их.

Попробовал посмотреть содержимое объекта...

var object = {};
object.key = object;
vardump(object);
//Прежде чем это делать, следует сохранить всё что может пропасть, когда рухнет редактор/отладчик! (если, конечно, это будет только он)

... и таки да, задумался об ограничении числа уровней рекурсии. Наверное 20 хватит?

//Рекурсивное описание содержимого объекта obj через WScript.Echo()
//obj - исследуемый объект
//padding - начальный отступ, в padding_char единицах (padding_char="  ")
//parent_name - имя объекта (произвольное, по сути комментарий)
//nesting - максимальный уровень вложенности объектов/значений
//Использование: vardump(myObj, 2, "Объект №5");
//Можно проще: vardump(myObj);
function vardump(obj, padding, parent_name, nesting)
{
  if (typeof(padding) == "undefined") //Определяем, если не определено (опущено при вызове)
    padding = 0;  //Отступ
  if (typeof(parent_name) == "undefined") 
    parent_name = ""; //Имя родителя
  if (typeof(nesting) == "undefined") 
    nesting = 20; //Максимальный исследуемый уровень вложенности по ум.
  var no_space_for_first = (parent_name == "") ? "" : " "; //Чтобы у корневого (безымянного) объекта не было пробела перед типом
  var padding_char = "  ";
  var pre_string = "";
  for (var i=0; i<padding; i++) //Набираем отступ
    pre_string += padding_char;

  if (nesting >= 0)
  {
    WScript.Echo(pre_string + parent_name + no_space_for_first +"(" + typeof(obj) + ")=>" + obj + "<"); //Выводим имя (тип)=>значение<
    if (typeof(obj) == "object")
    {
      for (var child in obj)
      {
        var child_name = parent_name; //Делаем имя потомку
        if (child_name != "")
          child_name += ".";
        child_name += child; 
        arguments.callee(obj[child], padding+1, child_name, nesting-1);
      }
    }
  }
  else
    WScript.Echo(pre_string+ "!!! Nesting limit reached !!!");
} //vardump

Вариант с возвращаемой строкой:

//Возвращает string: рекурсивное описание содержимого объекта obj
//obj - исследуемый объект
//padding - начальный отступ, в padding_char единицах (padding_char="  ")
//parent_name - имя объекта (произвольное, по сути комментарий)
//nesting - максимальный уровень вложенности объектов/значений
function svd(obj, padding, parent_name, nesting)
{
  if (typeof(padding) == "undefined") //Определяем, если не определено (опущено при вызове)
    padding = 0;  //Отступ
  if (typeof(parent_name) == "undefined") 
    parent_name = ""; //Имя родителя
  if (typeof(nesting) == "undefined") 
    nesting = 20; //Максимальный исследуемый уровень вложенности по ум.
  var no_space_for_first = (parent_name == "") ? "" : " "; //Чтобы у корневого (безымянного) объекта не было пробела перед типом
  var padding_char = "  ";
  var pre_string = "";
  for (var i=0; i<padding; i++) //Набираем отступ
    pre_string += padding_char;
  var result;
  if (nesting >= 0)
  {
    result = pre_string + parent_name + no_space_for_first +"(" + typeof(obj) + ")=>" + obj + "<\n"; //Выводим имя (тип)=>значение<
    if (typeof(obj) == "object")
      for (var child in obj)
      {
        var child_name = parent_name; //Делаем имя потомку
        if (child_name != "")
          child_name += ".";
        child_name += child; 
        result += arguments.callee(obj[child], padding+1, child_name, nesting-1);
      }
  }
  else
    result = pre_string+ "!!! Nesting limit reached !!!\n";
  return(result);
} //svd

Спасибо за маячок.

4 (изменено: Rumata, 2010-04-20 19:47:42)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Если честно, я не совсем пониманию именование функции - svd. S - ???, V - variable, D - dump. Так? Ну это придирка. Если Вам так удобно - я не имею ничего против, но если бы я или кто-то другой начал пользоваться Вашей функцией, то наверняка заинтересовался бы ее названием.

У каждого свое видение как форматировать типизацию объектов. Кто-то прописываете тип, кто-то указывает кавычки, кто-то еще что-то, лишь бы было понятно, что чем является.
Но вот с рекурсией - это первое, о чем я подумал, когда реализовывал когда-то аналогичную функцию. Сегодня проверил и понял, что у меня неправильно обрабатывается выход из рекурсии. Исправил недочет.

Просто при отладке сценариев для WSH vardump(obj) писать удобней чем WScript.Echo(vardump(obj))

Мне кажется, что такое лучше сделать в виде двух функций - как реализовать вариантов много. Например:

function vardump(...)
{
...
};

function varprint()
{
    WScript.Echo(vardump.apply(arguments, arguments));
};

Дополнение
Заметил в примере опечатку - исправил.

( 2 * b ) || ! ( 2 * b )

5

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

OFF:

jite пишет:

Хотя с недавнего времени осознал, что все-таки WScript.Echo() неудобно: wscript задалбывает ОК-окошками, а для cscript надо отдельно консоль открывать, чтобы узнать, чего он там понаписал.

Хмм. Я, например, давно осознал, что писать надо именно WScript.Echo(). Исполняемый хост сразу переназначен на cscript.exe. Консоль мне отдельно открывать не надо — я в ней всегда (Far Manager). Лог делается банальным перенаправлением в файл без плясок с разными бубнами (ну, или, через аналог tee.exe).

Не понимаю, отчего все так консоль ненавидят .

6

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Да, видимо лучше в несколько функций: основная неизменная vardump(), возвращающая string, и прочие - обертки для вывода.

О названиях. Насколько понимаю, это уже некоторая межъязыковая традиция, называть подобное "vardump" (php, javascript, perl). Начав знакомство с JScript, первым же делом занялся ее поиском/доработкой. Вот так 1-й вариант - с Echo() - и получил свое название. Потом понадобился "строчный", логично (как вначале показалось) получивший имя StringVarDump, которое в свою очередь пошло по наклонной -> sVarDump -> sVD -> svd.
Ну согласитесь, функция отладочная, причем очень ходовая, странно было бы ее названию не сократиться, будучи на порядок более раз написанной, чем прочие.
При публикации взял код из своих примеров, не особо задумываясь над названием, переименовать можно как угодно, рекурсия от этого не испортится.

vardump.apply(arguments, arguments).print();

print? Это уже из какого-то другого языка?

TODO
Научить бы ее еще ковырять коллекции. Думаю, с оценкой попыток в блоках try это дело вполне реализуемое.

7 (изменено: Rumata, 2010-04-20 20:05:31)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

print? Это уже из какого-то другого языка?

Нет. Из моих собственных расширений. Я исправил тот пост. Посмотрите его еще раз.

Еще одно дополнение

  var pre_string = "";
  for (var i=0; i<padding; i++) //Набираем отступ
    pre_string += padding_char;

Можно еще так

  var pre_string = (new Array(padding + 1)).join(padding_char);

А вообще-то надо передавать не размер отбивки, а саму отбивку, которая будет при рекурсивном вызове увеличиваться на один размер. В принципе для этих целей можно использовать parent_name. Представляете, что для каждого вложенного элемента объекта отбивка пересчитывается! Очень затратно!

function vardump(obj, nesting, parent_name)
{
...
        result += arguments.callee(obj[child], nesting-1, parent_name + child_name + padding_char);
...
( 2 * b ) || ! ( 2 * b )

8

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

jite пишет:

...
TODO[/i] Научить бы ее еще ковырять коллекции. Думаю, с оценкой попыток в блоках try это дело вполне реализуемое.

О каких коллекциях идет речь?

( 2 * b ) || ! ( 2 * b )

9

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Хмм. Я, например, давно осознал, что писать надо именно WScript.Echo(). Исполняемый хост сразу переназначен на cscript.exe. Консоль мне отдельно открывать не надо — я в ней всегда (Far Manager). Лог делается банальным перенаправлением в файл без плясок с разными бубнами (ну, или, через аналог tee.exe).

Не понимаю, отчего все так консоль ненавидят smile.

Да, на своей машине и обработчик можно настроить, и в Far'е работой наслаждаться (приветствую единомышленника!) Но ведь есть они - другие машины, на которых ничего этого нет и не предвидится.
<off> В моем случае масса SRP-ограничений и даже запуск cmd под обычным юзером запрещен, вот так вот все сурово и беспощадно. И еще местами 7-ка с ее доп. предупреждениями и глюками, чтоб ее...</off>
А отладку на не своей машине не под админом делать приходится... Я конечно знаю, что есть runas, методики правильнй настройки доменных политик, методы автоперезапуска скрипта под конкретным обработчиком, средства удаленного запуска и много чего еще в помощь... Но в некоторых, особо запущенных случаях эффективней специально подготовить скрипт и запускать на дефолтных настройках, а уже у себя "дома" разбираться с логами.

Так что Echo() плох не от того, что кто-то тут консоль не любит, просто условия и задачи у всех разные - иногда Echo просто не подходит. Так что, повторюсь, делать vardump() выдающей строку - очень даже мудрая мысль, ибо решение универсальное на всех хостах и во всех ситуациях.

10

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Rumata пишет:

О каких коллекциях идет речь?

Которые через Enumerator добываются: for (var en = new Enumerator(ОбъектРодитель); !en.atEnd(); en.moveNext()).
Буквы лог. дисков, содержимое папок и пр.

11 (изменено: Rumata, 2010-04-20 21:22:44)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Я так и подумал. Но... Зачем блоки try? И как Вы собираетесь оценивать попытки? Переведите коллекцию в массив или объект и работайте уже с ними. http://code.google.com/p/jsxt/source/br … merator.js

( 2 * b ) || ! ( 2 * b )

12 (изменено: jite, 2010-04-20 23:09:43)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Rumata пишет:

Я так и подумал. Но... Зачем блоки try? И как Вы собираетесь оценивать попытки? Переведите коллекцию в массив или объект и работайте уже с ними. http://code.google.com/p/jsxt/source/br … merator.js

Вероятно, ввиду скудости знаний. Ход рассуждений был следующий: если объект, опросить его свойства, а также в try-блоке попытаться взять у него enumerator - вдруг даст? Ну и далее перебирать... Впрочем - вспомнил свои попытки - это тупиковый путь. Системные объекты типа WScript.CreateObject("Scripting.FileSystemObject").GetFolder(...) не предоставляют vardump'у "свойства" (например .SubFolders), которые можно бы было взять за Enumerator.

С одной стороны хотелось бы получить универсальный показыватель содержимого чего угодно...
А с другой, если подумать, вот возьмем мы WScript.CreateObject("Scripting.FileSystemObject").GetFolder("имя какой-нить конечной папки с парой файлов") и каким нибудь особым образом добудем все-все его свойства/объекты/перечислятели. Так по ветке ParentFolder наверняка упремся в предел вложений, причем наверняка с немалыми временными затратами и без особого толку. М-да, что-то здесь не так...

А вот на счет набора отступов. Действительно, зачем тратить ресурсы на набор? Передавать! И - минус лишний параметр.
Вариант vardump() с WScript.Echo() отбрасывается как ненужный - причины см. выше.

//Возвращает строку рекурсивного описания содержимого объекта obj
//obj - исследуемый объект
//parent_name - имя объекта, произвольное, по сути комментарий (можно опустить)
//nesting - максимальный уровень вложенности объектов/значений (можно опустить)
function vardump(obj, parent_name, nesting)
{
  var padding_char = "  "; //Структурный отступ (на него сдвигаем потомков отн. родителей)
  if (typeof(padding) == "undefined") //Определяем опущенное при вызове
  if (typeof(parent_name) == "undefined") 
    parent_name = ""; //Имя родителя
  if (typeof(nesting) == "undefined") 
    nesting = 20; //Максимальный исследуемый уровень вложенности по ум.
  var no_space_for_first = (parent_name == "") ? "" : " "; //Чтобы у корневого (безымянного) объекта не было пробела перед типом
  var result;
  if (nesting >= 0)
  {
    result = parent_name + no_space_for_first + "(" + typeof(obj) + ")=>" + obj + "<\n"; //Выводим имя (тип)=>значение<
    if (typeof(obj) == "object")
      for (var child in obj)
      {
        var child_name = padding_char + parent_name; //Имя потомка: сдвиг + родитель ...
        if (child_name != padding_char) //Если есть родитель
          child_name += "."; //то имя через точку
        child_name += child; 
        result += arguments.callee(obj[child], child_name, nesting-1);
      }
  }
  else
    result = parent_name + " !!! Nesting limit reached !!!\n";
  return(result);
} //vardump

//Обертка вывода
function varecho(obj, parent_name, nesting)
{
  WScript.Echo(vardump(obj, parent_name, nesting));
}

Вот этого не понял. Есть какая-то польза?

function varprint()
{
    WScript.Echo(vardump.apply(arguments, arguments));
};

<off>Знаком с полным перечнем стандартных методов jscript - всё понятно... за исключением call() и apply(). Предназначение этих до сих пор для меня загадка. И тут как раз ОНО в примере.
ЧТО ЭТО?! Документация MS крайне скудна, даже примеров не дали. Поиск по форуму разъяснений по "apply(" и "call(" не дает, зато есть ссылки об использовании обеих вами. Может по этому вопросу лучше открыть новый тред?</off>

13

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

jite пишет:

...Вот этого не понял. Есть какая-то польза?
...
Знаком с полным перечнем стандартных методов jscript - всё понятно... за исключением call() и apply()...

Давайте я здесь расскажу, а потом коллегиально решим надо ли это выводить в отдельный топик. Но рассказывать я буду долго и нудно. Стиль у меня такой. (-:

Разные ресурсы описывают полезность этих методов по-разному. Я попытаюсь это сделать по-своему. Для начало официоза. Есть документация Windows Script Technologies, скачанная с сайта MS (простите, не могу дать прямую ссылку - этот chm-файл был скачан давно и путешествует со мной уже много лет по многим компьютерам). Она говорит ( в оригинале - на английском, но здесь - в моем переводе):

Вызывает метод объекта, подставляя другой объект для текущего объекта.
call([thisObj[, arg1[, arg2[,  [, argN]]]]])

Применяет метод объекта, подставляя другой объект для текущего объекта.
apply([thisObj[,argArray]])

Объяснение это - "просто курам на смех". Прочитав его, понять невозможно. А пример очень мало. Пойдем дальше.

Был такой ресурс "Солнечный ветер" (suncloud.ru). Он почил уже в бозе, поэтому прямых ссылок не даю - ибо материал там не обновлялся автором примерно лет 8, а домен никем не продлен. Однако ценности информация оттуда не потеряла. И так, там сказано (цитирую со страниц, скачанных давным-давно):

Метод call
Синтаксис: функция.call(объект[,аргументы]?)
Аргументы: объект — любое объектное выражение
                  аргументы — список аргументов функции через запятую
Результат: определяется функцией

Метод call вызывает метод одного объекта в контексте другого объекта. Точнее говоря, он вызывает функцию, задавая в качестве this указатель на объект, а в качестве фактических аргументов — аргументы. Если объект равен null или undefined, то значением this является глобальный объект. Если аргументы не указаны, то функция вызывается без аргументов.

Метод apply
Синтаксис: функция.apply(объект[,массив]?)
Аргументы: объект — любое объектное выражение
                  массив — массив аргументов функции
Результат: определяется функцией

Метод apply применяет метод одного объекта в контексте другого объекта. Точнее говоря, он вызывает функцию, задавая в качестве this указатель на объект, а в качестве фактических аргументов — элементы массива. Если объект равен null или undefined, то значением this является глобальный объект. Если массив не указан, равен null или undefined, то функция вызывается без аргументов.

Более понятно, но не очень. Я не буду углубляться в цепочки конструкторов в качестве примера. А рассмтотрю один реальный случай. Ваш. И так. есть функция vardump, которая обрабатывает некоторую переменную и возвращает строковое представление данной переменной:

function vardump(object, nesting, padding)
{
...
    return result;
};

Функция сложная, использует различные методики, и объект любой сложности трансформирует в строку. Строку непростую, являющуюся "слепком" оригинального объекта. Все бы хорошо, но нам нужна еще одна функция, которая делает слепок и печатает его. Что мы можем сделать? Конечно "продублировать ее:

function varprint(object, nesting, padding)
{
    // Я использовал абстрактную функцию print() чтобы не привязываться конкретно к WSH. 
    // Представим, что мы где-то выше по коду определили такую функцию. 
    print(vardump(object, nesting, padding));
};

Неплохо. Но представим, что мы еще отлаживаем эту функцию. Возможно, что количество параметров передаваемых в vardump изменится. Возможно, мы изменим их порядок. В таком случае, мы должны быть готовы отредактировать не только функцию vardump, но и varprint, котороая зависит от первой. Это приведет к тому, что мы что-то изменил в vardump, но забудем изменить в varprint. Возможно, что мы можем помнить все и заблаговременно исправить и вторую функцию, но для этого надо держать в памяти зависимость одной функции от другой. Так вот, от всего этого спасают два простых метода - call и apply.

call и apply вызывают некий метод/функцию не привычным способом, а так как будто говорим "сделай то, что ты делаешь, но изменения будут сделаны у меня".
call - в качестве аргументов передается некоторый список.
apply - в качестве аргументов передается массив аргументов (например, список аргументов вызванной функции).

В Вашем случае, vardump и varprint - функции имеющие одинаковый набор аргументов, потому лучше всего использовать метод apply (применить набор аргументов одной функции к другой). Все это шаманство приводит к такому коду

function varprint()
{
    var result = vardump.apply(arguments, arguments);
    WScript.Echo(result);
};

Что все это значит? А вот что. Не задумываясь о количестве и порядке аргументов функциях varprint и vardump мы смело вызываем varprint, которая корректно передаст свой список аргументов функции vardump, от которой зависит. И таким образом мы обезопасили себя от неизбежной забывчивости, оптимизировали свой труд, узаконили соглашение двух зависимых функций о порядке передаваемых аргументов.

Вроде бы все. Если у Вас будут вопросы - задавайте.

( 2 * b ) || ! ( 2 * b )

14 (изменено: jite, 2010-04-21 11:44:03)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Кажется, понял. Несомненно полезно использовать "arguments" вместо их прямого перечисления.
Удивительно:
- пока разбирался, конструкция с apply никак не хотела работать, и я никак не мог понять, в чем причина?
- когда разобрался, конструкция стала работать как ни в чем не бывало... но теперь я не могу понять, как я вначале умудрился написать ее так, чтобы она не работала?

Замечание по осмыслении. Насколько понял, 1-м параметром у apply() идет объект, который в обычном применении стоял бы перед точкой:

//Выражение:
MyFunction.apply(MyObj, arguments);
//выглядит "в обычном исполнении" как:
MyObj.MyFunction(аргументы-через-запятую);

Тогда для статической обертки правильней было бы записать:

vardump.apply(null, arguments)

Таким образом, обертка будет:

function varecho()
{
  WScript.Echo(vardump.apply(null, arguments));
}

Меня это двойное "(arguments, arguments)" сильно с толку сбивало, впрочем это никак не отражается на работе скрипта.

15 (изменено: Rumata, 2010-04-21 13:30:56)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

jite пишет:

...
Замечание по осмыслении. Насколько понял, 1-м параметром у apply() идет объект, который в обычном применении стоял бы перед точкой:

//Выражение:
MyFunction.apply(MyObj, arguments);
//выглядит "в обычном исполнении" как:
MyObj.MyFunction(аргументы-через-запятую);

...

Возможна такая трактовка. А так же:

//Выражение:
MyFunction.apply(MyObj, arguments);
//выглядит "в обычном исполнении" как:
MyObj(аргументы-через-запятую);

call/apply методы позволяют вызвать методы одного объекта в контексте другого, или одни функции в контексте параметров другой (наш случай). Вот реальный пример. Список аргументов доступен с помощью объекта arguments, у него есть свойство length - количество переданных функции аргументов, доступ к элементам выполняется по числовому ключу. Но это не массив. То есть к нему не применимы методы типа Array. Следующий пример создает массив из объекта arguments:

// К объекту arguments применен метод slice объекта Array, как будто вызов arguments.slice()
var args = Array.prototype.slice.call(arguments);

// Пример выше короче и уже привычен в мире js-программистов
// А ниже классический пример

var args = [];
for (var i = 0; i < arguments.length; i++) {
    args[i] = arguments[i];
}

Дополнение: Внес небольшое, но существенное исправление

( 2 * b ) || ! ( 2 * b )

16

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

По прошествии времени - специально решил подождать, пока все уляжется в голове,- стало очевидно, что иногда очень полезно бывает передать Саше Машину задачу:
Маша.apply(Саша, задача)
если
а) Саша ее может сделать лучше
б) Саша - обертка и активно использует Машу

Вместе с тем совершенно потерялся смысл различия apply и call. Если даже посмотреть описания - один в один кроме начала "Applies a method of an object" и "Calls a method of an object" Ну никакой смысловой разницы между "применением" и "вызовом" не ощущаю. Ну ведь синонимы же!

17 (изменено: Rumata, 2010-04-21 22:42:55)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Согласен. Граница еле различимая. Но отличие существенная. Все-таки "вызвать" и "применить" - разные действия. Попробую использовать Ваши аналогии (я их тоже люблю, в этот раз воспользуюсь Вашими, но с небольшими поправками)

// Надо чтобы Саша помог выполнить дела Маше: 
Маша.делает(дело1, дело2, дело3)

// 1. 
// Маша передает весь список дел Саше:
Маша.делает.apply(Саша, дела)

// Как если бы было
Саша.делает(дело1, дело2, дело3)

// 2. 
// Маша передает часть дел Саше:
Маша.делает.call(Саша, дело1, дело2, дело3)

// Как если бы было
Саша.делает(дело1, дело2, дело3)
( 2 * b ) || ! ( 2 * b )

18 (изменено: jite, 2010-04-21 23:02:33)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Проверил на известном примере:

function varecho()
{
  WScript.Echo(vardump.apply(null, arguments)); //исходный
  //WScript.Echo(vardump.call(null, arguments)); //не работает, верней работает, но неправильно - анализирует не 1-й элемент, а сам массив аргументов
  //WScript.Echo(vardump.call(null, arguments[0], arguments[1], arguments[2])); //работает
  //WScript.Echo(vardump.call(null, arguments[0])); //работает, передавая только 1-й аргумент
}

Да, видимо они так и отличаются - по логике substr() и substring(). Наконец, обратил внимание на синтаксис:

apply([thisObj[,argArray]])
call([thisObj[, arg1[, arg2[,  [, argN]]]]])

Большое спасибо за подробные пояснения!

19 (изменено: jite, 2010-11-14 21:46:10)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

А делается apply() в VBScript?

upd
Нашел: стандартно - никак.

20 (изменено: jite, 2011-04-16 21:14:32)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Расширенная и дополненная версия vardump():
- отличает массивы, показывает length
- укорачивает (при необходимости, по желанию пользователя) слишком длинные строки представления массивов/объектов со множеством элементов и свойств (все равно они в полном объеме будут представлены ниже в структуре)
- более наглядно показывает код функций
- работает не только под WSH JScript, но и под JavaScript (проверялась в IE8, Firefox, Chrome, Opera, Safari)
Основная цель доработки: обеспечение ясности и полноты представления свойств даже сложных объектов.

/** Возвращает строку рекурсивного описания содержимого объекта oBj
 * @param sParentName - имя объекта, произвольное, по сути комментарий (можно опустить)
 * @param nEsting - максимальный уровень вложенности объектов/значений (можно опустить)
 */
function vardump(oBj, sParentName, nEsting) {
  var nl = "\r\n"; //Перенос строки
  var nEsting_max = 12; //Максимальный исследуемый уровень вложенности, против бесконечных ссылок на самого себя
  var nPadding = 2; //Структурный отступ сдвига потомков относительно родителей, в пробелах
  //Ограничивать ли слишком длинное отображение объекта в поле >значения<. Во избежание захламления вывода чрезмерно 
  //длинными строками объектов с большим числом свойств и элементов, рекомендуется установить true
  var bRestrictObjectVal = true, nRestrictWidth = 80, sCutSign = "..."; //Знак ограничения длины отображения
  if (sParentName == undefined) sParentName = "";
  if (isNaN(parseInt(nEsting))) nEsting = nEsting_max;
  var sRes = "";
  for ( var i = 0; i < nPadding * (nEsting_max - nEsting); i++)
    sRes += " ";
  if (nEsting >= 0) {
    sRes += sParentName + " (";
    sRes += oBj instanceof Array ? "array[" + oBj.length + "]" : typeof oBj;
    sRes += ")=>";
    if (typeof oBj == "function") //Функции в этой позиции не отображаем
    sRes += sCutSign;
    else
      //Строковое представление объектов: с ограничениями (без переносов строки и в пределах nRestrictWidth зн.)
      if (oBj instanceof Object && bRestrictObjectVal && !(oBj instanceof Date)) {
        var nMaxOTSLength = nRestrictWidth - sRes.length - 1; //-1 - место под завершающую <
        var nCutOSTLength = nMaxOTSLength - sCutSign.length;
        if (nCutOSTLength < 0) nCutOSTLength = 0;
        var sOTS = oBj.toString().replace(/\r/g, "\\r").replace(/\n/g, "\\n"); //Меняем переносы строки на их "символы"
        if (sOTS.length > nMaxOTSLength) sOTS = sOTS.substr(0, nCutOSTLength) + sCutSign;
        sRes += sOTS;
      }
      else
        sRes += oBj; //Строковое представление объектов: без ограничений
    sRes += "<" + nl;
    switch (typeof oBj) {
    case "object":
      for ( var oEl in oBj) {
        var sObjName = sParentName; //Имя потомка
        if (sObjName != "") //Если есть что отделять, отделяем точкой
        sObjName += ".";
        sObjName += oEl;
        sRes += arguments.callee(oBj[oEl], sObjName, nEsting - 1);
      }
      break;
    case "function": //Код функции обрамляем >>> <<< для наглядности
      sRes += ">>>" + nl + oBj + "<<<" + nl;
      break;
    }
  }
  else
    sRes += sParentName + " ! Nesting limit !" + nl;
  return(sRes);
}

/** Echo-обертка для vardump()
 * В javascript "WScript.Echo" заменить на подходящий аналог вывода (в лог отладки или прямо на страницу в тэге <pre>)
 */
function vard() {
  WScript.Echo(vardump.apply(null, arguments));
}

Код демонстрации возможностей:

var myArr = [];
myArr[0] = "string 1234 ";
myArr[1] = function() {
  return a + b;
};
myArr[2] = {a: 32, b: "v4", c: [1, "a"], d: "123".substr(0, 0)};
myArr[-1] = -2; //Не войдет в индекс (и не отразится в length), а будет записано как свойство 
myArr[3] = [98, "str", function() {
  x++;
}, {prop: "value"}];
//myArr[6] = myArr; //Раскомментируйте, чтобы увидеть поведение при циклических ссылках объекта на себя и при отображении сложных структур
myArr[5] = new Date();

vard(myArr, "myArr");

Под WSH выведет:

myArr (array[6])=>string 1234 ,function() {\r\n  return a + b;\r\n},[object ...<
  myArr.0 (string)=>string 1234 <
  myArr.1 (function)=>...<
>>>
function() {
  return a + b;
}<<<
  myArr.2 (object)=>[object Object]<
    myArr.2.a (number)=>32<
    myArr.2.b (string)=>v4<
    myArr.2.c (array[2])=>1,a<
      myArr.2.c.0 (number)=>1<
      myArr.2.c.1 (string)=>a<
    myArr.2.d (string)=><
  myArr.-1 (number)=>-2<
  myArr.3 (array[4])=>98,str,function() {\r\n  x++;\r\n},[object Object]<
    myArr.3.0 (number)=>98<
    myArr.3.1 (string)=>str<
    myArr.3.2 (function)=>...<
>>>
function() {
  x++;
}<<<
    myArr.3.3 (object)=>[object Object]<
      myArr.3.3.prop (string)=>value<
  myArr.5 (object)=>Sat Apr 16 23:06:00 UTC+0600 2011<

21

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

О! У Вас новая версия. Поздравляю.
А я в своей версии этой функции так и не понял - надо конвертировать некоторые символы в строковых переменных или нет. Имею в виду специальные символы (\r, \n, \t в консоли и &, <, > в браузере). Поэтому соответствующие строки просто закомментировал.

oBj, sParentName, nEsting

Очень странные имена переменных. Вам так удобно?

( 2 * b ) || ! ( 2 * b )

22 (изменено: mikser, 2011-04-16 23:47:06)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Много букв.
Можете в кратце объяснить чем ваще детище лучше чем уже довольно давно существующее решение - DataDumper ?
PS Формат вывода у вас очень замудреный, чем вас не устроил простой JSON?

23 (изменено: Rumata, 2011-04-17 00:37:59)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

mikser пишет:

Много букв.
Можете в кратце объяснить чем ваще детище лучше чем уже довольно давно существующее решение - DataDumper ?
PS Формат вывода у вас очень замудреный, чем вас не устроил простой JSON?

Я не могу согласиться с Вашим первым утверждением
- предложенная пользователем jite функция гораздо короче
- использует не внешние переменные для управления, а параметры, передаваемые в функцию
- JSON.stringify отображает все в строку. И хотя она все еще читабельна, но требует усилия для ее чтения глазами и предназначена для обмена между машинами, также как и XML, а не для чтения глазами
- упомянутый DataDumper в первую очередь "заточен" под браузеры - падает в консоли и требует дополнительных телодвижений
- неправильно определяет массивы - попробуйте посмотреть следующий код var x = {length: 1000}.

mikser, я с Вами полностью согласен - вывод очень странный и непривычный. В этом смысле вывод функций var_dump и print_r из PHP гораздо удобнее и понятнее

( 2 * b ) || ! ( 2 * b )

24

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Rumata пишет:

О! У Вас новая версия. Поздравляю.

Спасибо! Просто почитав Флэнагана решил упростить некоторые выражения... в результате код слегка усложнился, но в целом результат вроде положительный.

Rumata пишет:

А я в своей версии этой функции так и не понял - надо конвертировать некоторые символы в строковых переменных или нет. Имею в виду специальные символы (\r, \n, \t в консоли и &, <, > в браузере). Поэтому соответствующие строки просто закомментировал.

Честно говоря, даже не задумался, как браузеры будут справляться с отображением многочисленных < и >. Наверное потому что все справились без проблем.
Здесь наверное стоит упомянуть, каким именно образом в тестах выводился текст: весь вывод в скрипте сбрасывался в переменную txt а затем:

document.getElementById("main").innerHTML = '<pre>\n' + txt + '\n</pre>';

И всё - более никаких преобразований. Оно "как-то само собой" благополучно показывается. Firefox через FireBug честно показал наличие преобразований & > < в сущности &gt; &lt; &amp;. Остальные браузеры не признаются, показывая исходный текст как бы "как видится", но вместе с тем четко отделяют его от тегов. Очевидно, каждый браузер выполняет преобразование автоматически. Кстати, кодировка страницы на результат не влияет, а кириллические символы тоже благополучно выводятся. Из этого делаю вывод, что в скрипте о дополнительных преобразованиях беспокоиться не требуется, по крайней мере в случае вывода сгенерированного скриптом текста.

А преобразование символов перевода строки сделано было лишь для обеспечения представления объектов toString() в одну строчку, дабы эти самые переводы не нарушали структуру вывода, путая все и вся. Остальное не трогал. Да, при необходимости, пожалуй, можно еще визуализировать символы табуляции в какое-нибудь "\\t".

Rumata пишет:

oBj, sParentName, nEsting

Очень странные имена переменных. Вам так удобно?

А как лучше?
Ну, думаю, понятно, что значат первые маленькие буквы: o - object, n - number, s - string, b - boolean и т.д. А oBj и nEsting - просто сокращения полных форм oObj и nNesting. Иногда встречаются исключения, как правило традицiонные/частичноСокращенныеJS/СтилистическиСлегка_смешанные/ИиЗрЕдкАпРоСтОдУрАцКиЕ (i, sOTS, nEsting_max, nl), а еще заимствования из чужого кода (myArr). Но в основном стараюсь придерживаться lowertypeCamelCase.

25

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Говоря об управляющих символах я имел в виду следующее:


vardump('строка в <br />браузере');
vardump('строка в \nконсоли');

То есть, необходимо ли преобразование символов \n, <, > (и прочих подобных) в их "читабельные эквиваленты", как то комбинации символов "\n", "&lt;", "&gt;".

( 2 * b ) || ! ( 2 * b )

26

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Rumata пишет:

То есть, необходимо ли преобразование символов \n, <, > (и прочих подобных) в их "читабельные эквиваленты", как то комбинации символов "\n", "&lt;", "&gt;".

Думаю, не стоит ставить перед vardump() задачу форматирования вывода, это уже задачи оберток. Впрочем есть возможность сделать что-то с переносами строки уже на данном этапе - это переменная nl.

mikser пишет:

Много букв.
Можете в кратце объяснить чем ваще детище лучше чем уже довольно давно существующее решение - DataDumper ?
PS Формат вывода у вас очень замудреный, чем вас не устроил простой JSON?

Это оно для вас "давно существующее". И только под браузер заточенное. Как ознакомлюсь - сравню.
А в DataDumper мало букв?

И да, я понял, >ограничители значений< наверное многим покажутся лишними/странными. Изначально не хотел использовать кавычки или оставлять значения просто так. Значит все-таки сделать эти ограничители опциональными?
Ну а указание типа - это разве лишнее?
Хм, print_r и JSON, говорите... Надо подумать.

27

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Ограничители нужны для строк, чтобы видеть конец строки. Поэтому - или кавычки, или апострофы. JSON использует кавычки. Он кстати преобразует переводы строк и табуляции.

А JSON... В ФФ он есть кажется начиная с каких-то версии 3+. В МСИЕ он встроен то ли с версии 8, то ли 9. В WSH 5.7 его тоже нет (Windows XP Professional SP3, Windows Vista Enterprise SP2).

( 2 * b ) || ! ( 2 * b )

28 (изменено: jite, 2011-04-18 01:15:03)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Версия "Долой лишние разделители!"

/** Возвращает строку рекурсивного описания содержимого объекта oBj
 * @param sParentName - имя объекта, произвольное, по сути комментарий (можно опустить)
 * @param nEsting - максимальный уровень вложенности объектов/значений (можно опустить)
 */
function vardump(oBj, sParentName, nEsting) {
  var sNL = "\r\n", /* Перенос строки */ sLQ = ">", sRQ = "<"; //Левый и правый ограничители значения 
  var nEsting_max = 12; //Максимальный исследуемый уровень вложенности, против бесконечных ссылок на самого себя
  var nPadding = 2; //Структурный отступ сдвига потомков относительно родителей, в пробелах
  //Ограничивать ли слишком длинное отображение объекта в поле >значения<
  var bRestrictObjectVal = true, nRestrictWidth = 80, sCutSign = "...";
  if (sParentName == undefined) sParentName = "";
  if (isNaN(parseInt(nEsting))) nEsting = nEsting_max;
  var sRes = "";
  for ( var i = 0; i < nPadding * (nEsting_max - nEsting); i++)
    sRes += " ";
  if (nEsting >= 0) {
    sRes += sParentName + "  ";
    sRes += oBj instanceof Array ? "array[" + oBj.length + "]" : typeof oBj;
    sRes += "  " + sLQ;
    if (typeof oBj == "function") sRes += sCutSign; //Функции в этой позиции не отображаем
    else
      //Объект.toString() с ограничениями
      if (oBj instanceof Object && bRestrictObjectVal && !(oBj instanceof Date)) {
        var nMaxOTSLength = nRestrictWidth - sRes.length - 1; //-1 под <
        var nCutOSTLength = nMaxOTSLength - sCutSign.length;
        if (nCutOSTLength < 0) nCutOSTLength = 0;
        var sOTS = oBj.toString().replace(/\r/g, "\\r").replace(/\n/g, "\\n");
        if (sOTS.length > nMaxOTSLength) sOTS = sOTS.substr(0, nCutOSTLength) + sCutSign;
        sRes += sOTS;
      }
      else
        sRes += oBj; //Объект.toString() без ограничений
    sRes += sRQ + sNL;
    switch (typeof oBj) {
    case "object":
      for ( var oEl in oBj) {
        var sObjName = sParentName; //Имя потомка
        if (sObjName != "") sObjName += "."; //Если есть что отделять, отделяем точкой
        sObjName += oEl;
        sRes += arguments.callee(oBj[oEl], sObjName, nEsting - 1);
      }
      break;
    case "function":
      sRes += sLQ + sLQ + sLQ + sNL + oBj + sRQ + sRQ + sRQ + sNL;
      break;
    }
  }
  else
    sRes += sParentName + " ! Nesting limit !" + sNL;
  return(sRes);
}

Изменения минимальные - косметические... но да, отсутствие лишних разделителей определенно идет на пользу читаемости.
Было на выходе:

myArr.2 (object)=>[object Object]<
    myArr.2.a (number)=>32<
    myArr.2.b (string)=>v4<
    myArr.2.c (array[2])=>1,a<

Стало:

myArr.2  object  >[object Object]<
    myArr.2.a  number  >32<
    myArr.2.b  string  >v4<
    myArr.2.c  array[2]  >1,a<

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

myArr.2  object  "[object Object]"
    myArr.2.a  number  "32"
    myArr.2.b  string  "v4"
    myArr.2.c  array[2]  "1,a"

29

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Посмотрел DataDumper. Велосипед ранней партии.
Вот вывод Dumper(myArr)

[
 'string 1234& абв ',
 [function],
 {
  'a' => 32,
  'b' => 'v4',
  'c' => [
          1,
          'a'
         ],
  'd' => ''
 },
 [
  98,
  'str',
  [function],
  {
   'prop' => 'value'
  }
 ],
 [null],
 {}
]

Вывод vardump()

myArr  array[6]  >string 1234& абв ,function () {\n    return a + b;\n},[obj...<
  myArr.0  string  >string 1234& абв <
  myArr.1  function  >...<
>>>
function () {
    return a + b;
}<<<
  myArr.2  object  >[object Object]<
    myArr.2.a  number  >32<
    myArr.2.b  string  >v4<
    myArr.2.c  array[2]  >1,a<
      myArr.2.c.0  number  >1<
      myArr.2.c.1  string  >a<
    myArr.2.d  string  ><
  myArr.-1  number  >-2<
  myArr.3  array[4]  >98,str,function () {\n    x++;\n},[object Object]<
    myArr.3.0  number  >98<
    myArr.3.1  string  >str<
    myArr.3.2  function  >...<
>>>
function () {
    x++;
}<<<
    myArr.3.3  object  >[object Object]<
      myArr.3.3.prop  string  >value<
  myArr.5  object  >Mon Apr 18 2011 12:40:11 GMT+0600<

Особенность DataDumper - лаконичное отображение только значений. По ситуации обернется достоинством или недостатком.
Отсутствие у массивов индексов, думаю, все-таки больше недостаток, чем достоинство - даже на маленьких массивах начинаю путаться, где что.
Недостатки DataDumper:
- не показывает типы
- не показывает даты ("{}" это не информация) и вообще не показывает значения объектов toString() - они вполне могут быть информативными
- не показывает у массивов не-индексные элементы (т.е. свойства - элементы с именами не являющимися целыми положительными 32-битными числами)
- показывает пустые "[null]" элементы массивов - это пожалуй лишнее
- некорректно определяет массивы (массив это не то, что имеет свойство length целого типа, а то, что произошло от Array)
- "много букв" и внешних переменных, в то время как vardump() автономный моноблок, легко вставляемый и убираемый

Впрочем, все перечисленное не так уж и сложно исправить самостоятельно.

Не впечатлился как-то DataDumper. Наверняка есть и более мощные варианты дамперов под js... или браузерные отладчики настолько захватили власть? Подскажите, если знаете.

30

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

- не показывает типы

Показ типа переменной или элемента массива/объекта избыточно. Сравните:

"false"
false
"42"
42
{ ... }
[ ... ]

Во всех случаях понятно, что является строкой, числом, булевой, массивом или объектом.

JSON хорош, чтобы посмотреть сложную структуру развернуто:


var x = {
    a: false, 
    b: "false", 
    c: "42", 
    d: 42
};

// {"a":false,"b":"false","c":"42","d":42}
JSON.stringify(x);

// выведет тоже самое, но в несколько строк и с отступами в 4 пробела
JSON.stringify(x, null, 4);

// тоже самое, но указан заполнитель
JSON.stringify(x, null, '\t');

Всем понятно, для чего нужен JSON. И когда надо заглянуть под юбку сложной структуры данных, он тоже может помочь. Недостаток, в данном случае один - сложен в наборе (сравните с varpdump(x)). Правда, самоссылочные, циклические структуру ему тоже не по зубам. Но для этого он не предназначался. В ФФ JSON уже встроен, но и он не работает с рекурсивными данными.

- ... не показывает значения объектов toString() - они вполне могут быть информативными

Вообще-то перебор свойств объекта и вывод метода toString() могут очень сильно различаться.

- не показывает у массивов не-индексные элементы (т.е. свойства - элементы с именами не являющимися целыми положительными 32-битными числами)

Это пагубная тенденция хранить данные в массивах с нецифровыми индексами, также  и работать с элементами массива с помощью цикла for in.

есть и более мощные варианты дамперов под js

Так Вы его уже написали ))) Сам тоже пользуюсь своим самописным. Доволен )))

( 2 * b ) || ! ( 2 * b )

31 (изменено: jite, 2011-04-18 15:07:09)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Rumata пишет:

Это пагубная тенденция хранить данные в массивах с нецифровыми индексами, также  и работать с элементами массива с помощью цикла for in.

Ну про не-индексные свойства массивов когда узнал, я сам был в шоке.
Может быть тенденция и пагубная. Можно это использовать или нет, но дампер обязан это показывать, тем более что свойством индекс может оказаться по ошибке или незнанию.

Про показ типов. Да, был неправ. Но вот тип Date DataDumper явно упущен.
Вообще-то довольно изящное решение. Не сделать ли в vardump() так же? Осталось придумать особое оформление для Date и, возможно, RegExp.

А почему "for in" - пагубная тенденция?

32 (изменено: Rumata, 2011-04-18 15:54:34)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

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

По-моему, это сомнительный аргумент. На самом деле не так уж много случаев, когда массив содержит дополнительное свойство, но они всегда оговариваются:


var s = '123abc456';
var re = /\d+/g;

var x = s.match(re);
alert(x);
alert(Object.prototype.toString.call(x));
akert(x.lastIndex);

Про показ типов. Да, был неправ

Отчего же так категорично? Просто эта информация избыточна.

особое оформление для Date и, возможно, RegExp


var now = new Date();
alert(ow.toUTCString());

var re = /\d+/;
alert(re.source);

А почему "for in" - пагубная тенденция?

Часто бывает, что именно объект Array расширяют - добавляют методы, отсутствующие в JScript, но описанные в спецификации Javascript - ранней (indexOf, lastIndexOf) и посдней (forEach, map, filter и прочие). Поэтому они тоже попадут под обстрел for in. Что может оказаться не очень приятным.

( 2 * b ) || ! ( 2 * b )

33 (изменено: jite, 2011-04-18 17:46:51)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

jite пишет:

Про показ типов. Да, был неправ

Был неправ в части:

jite пишет:

Недостатки DataDumper:
- не показывает типы

Показывает типы, большей частью, хоть и неявно. Никакой категоричности, просто констатация.
А я осознал всю прелесть и тоже так хочу. Ну хотя бы попробовать. Главное, чтобы сохранялась исчерпывающая информативность и код функции остался достаточно компактным.

jite пишет:

особое оформление для Date и, возможно, RegExp

* для строк кавычки
* для номеров, логических значений и спецзначений (null, undefined, NaN и пр.) ничего - и так понятно
* массивы обрамлять как-то так:

arrName [length_циферкой]
[
  arrName[0] : значение
  arrName[2] : значение
  arrName.неиндексное_свойство : значение
]

объекты в общем виде примерно так же:

 objName >тут_будет_objName.toString()< 
{
  objName.prop1 : значение
  objName.prop2 : значение
}

* для функций видимо так

 fName >function<
>>>код<<<

* Date вот это как? ...
О! А если будет показывать без кавычек, то это получается - не строка, а дата!
К слову, с показом даты у браузеров если и не бардак, то все равно разброд и шатание буквально в каждой из функций вывода текстом отличия в тексте - специально проверял. Так что не суть, какую функцию за основу брать, главное чтобы в ней была полная дата и время и чтобы с GMT/UTC (это кто как пишет)
* RegExp

 RegexpName /pattern/ флаги

Словом что-то такое JSON-образное, но с полными "именами" элементов (вроде myObj.prop1[34].prop2) ибо мотать портянку вверх, вспоминая "чье ж ты?" не дело.

По расширяемой функциональности массивов. Это значит надо предусмотреть место в коде функции (пояснить комментами), где могли бы отлавливаться и соответственно обрабатываться пользовательские типы. Правда тянет уже на что-то большее, чем функцию, но может всё не так уж и сложно выйдет.

А про мощные дамперы. В идеале вместо текста это должна быть полноценная древовидная структура, сворачивающаяся плюсиками. Так что думаю, что-то такое уже наверняка наваяли.

34

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Это пагубная тенденция

Вообще-то, в целом это верно: объектам - for in, массивам for each.

Но в данном случае это не принципиально. Ведь надо просто показать, содержимое некоторой переменной любой степени сложности. А вот просматривать надо вот так:


for (var k in object) {
    if ( ! object.hasOwnProperty(k) ) {
        continue;
    }
    var v = ... object[k] ...;
}

Так мы исключаем из просмотра свойства прототипа объекта и просматриваем исключительно свойства самого объекта.

( 2 * b ) || ! ( 2 * b )

35 (изменено: jite, 2011-04-19 22:50:39)

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Понятно, учел. Вот реализация по озвученным чуть выше идеям. Обертка vard() - под WSH, но основная функция проверялась и под браузерами.

/** Возвращает строку рекурсивного описания содержимого объекта oBj
 * @param sParentName - имя объекта, произвольное, по сути комментарий (можно опустить)
 * @param nEsting - максимальный уровень вложенности объектов/значений (можно опустить)
 */
function vardump(oBj, sParentName, nEsting) {
  var sNL = "\r\n"; // Перенос строки 
  var nEsting_max = 12; //Максимальный исследуемый уровень вложенности, против бесконечных ссылок на самого себя
  var nPadding = 2; //Структурный отступ сдвига потомков относительно родителей, в пробелах
  //Ограничивать ли слишком длинное отображение объекта в поле >значения<
  var bRestrictObjectVal = true, nRestrictWidth = 80, sCutSign = "...";
  var bShowOnlyOwnProps = true; //Показывать только собственные (уникальные) свойства объекта или все приобретенные
  var bShowFunctionsCode = true; //Показывать ли код функций
  if (sParentName == undefined) sParentName = "";
  if (isNaN(parseInt(nEsting))) nEsting = nEsting_max;
  var sRes = "";
  for ( var i = 0; i < nPadding * (nEsting_max - nEsting); i++)
    sRes += " ";
  if (nEsting >= 0) {
    sRes += sParentName + "  ";
    switch (typeof oBj) { //Описание элемента toString() и обрамление в соответствии с его типом 
    case "string":
      sRes += "\"" + oBj + "\"";
      break;
    case "function":
      if (oBj instanceof RegExp) //В Chrome typeof RegExp => function
        sRes += oBj + "  as ";
      sRes += "function";
      break;
    case "object":
      if (oBj instanceof Array) {
        sRes += "Array[" + oBj.length + "]  ";
      }
      if (oBj instanceof Date || oBj instanceof RegExp) {
        sRes += oBj;
      }
      else { //Вывод прочих объектов и массивов
        sRes += ">";
        var sOTS = ("" + oBj).replace(/\r/g, "\\r").replace(/\n/g, "\\n");
        if (bRestrictObjectVal) { //Если надо, подрезаем слишком длинные описания в строку
          var nMaxOTSLength = nRestrictWidth - sRes.length - 1; //-1 под "<"
          var nCutOSTLength = nMaxOTSLength - sCutSign.length;
          if (nCutOSTLength < 0) nCutOSTLength = 0;
          if (sOTS.length > nMaxOTSLength) sOTS = sOTS.substr(0, nCutOSTLength) + sCutSign;
        }
        sRes += sOTS + "<";
      }
      break;
    default: //числа, спецзначения, логические
      sRes += oBj;
    }
    sRes += sNL;

    var sEl;
    var sGetChildName =
      function(oParent, sParentName, sChildName) {
        var sFullChildName = sParentName;
        var bIsIndex = false;
        if (oParent instanceof Array) {
          var nIdx = parseInt(sChildName, 10);
          if (!isNaN(nIdx) && nIdx.toString() === sChildName.toString() && nIdx >= 0 && nIdx < oParent.length)
            bIsIndex = true;
        }
        if (bIsIndex) sFullChildName += "[" + sChildName + "]";
        else {
          if (sFullChildName != "") sFullChildName += "."; //Если есть что отделять, отделяем точкой
          sFullChildName += sChildName;
        }
        return sFullChildName;
      }; //sGetChildName

    if (oBj instanceof RegExp) { //В зависимости от браузера typeof то object, то function
      sEl = "lastIndex";
      sRes += arguments.callee(oBj[sEl], sGetChildName(oBj, sParentName, sEl), nEsting - 1);
    }
    else { //Осмотрим внутренности
      switch (typeof oBj) {
      case "object": //Объекты и массивы
        for (sEl in oBj) {
          if (bShowOnlyOwnProps && !oBj.hasOwnProperty(sEl))
            break;
          sRes += arguments.callee(oBj[sEl], sGetChildName(oBj, sParentName, sEl), nEsting - 1);
        }
        break;
      case "function":
        if (bShowFunctionsCode) sRes += ">>>" + sNL + oBj + "<<<" + sNL;
        break;
      //Остальное содержимого не имеет
      }
    }
  }
  else
    sRes += sParentName + " ! Nesting limit !" + sNL;
  return(sRes);
}

/** Echo-обертка
 */
function vard() {
  WScript.Echo(vardump.apply(null, arguments));
}

Обновленный демонстрационный пример:

var arrMyPrettySample = [];
arrMyPrettySample[0] = "string 1234& абв ";
arrMyPrettySample[1] = function() {
  return a + b;
};
arrMyPrettySample[2] = {a: 3.2, b: "v4", c: [1, "a"], d: ""};
arrMyPrettySample[-1] = -2; //Не войдет в индекс (и не отразится в length), а будет записано как свойство 
arrMyPrettySample[3] = [98, false, function() {
  x++;
}, {prop: "value"}];
//arrMyPrettySample[6] = arrMyPrettySample; //Расскомментировать для теста циклических ссылок
arrMyPrettySample[30] = null;
arrMyPrettySample[31] = NaN;
arrMyPrettySample[32] = undefined;
arrMyPrettySample[33] = Infinity;
arrMyPrettySample[55] = new Date();
arrMyPrettySample[44] = new RegExp("123", "g");

Array.prototype.prop1 = ":)"; //Внедренное в прототип массива доп. свойство (не показывается при bShowOnlyOwnProps true)

vard(arrMyPrettySample, "arrMyPrettySample");

Выведет:

arrMyPrettySample  Array[56]  >string 1234& абв ,function() {\r\n  return a ...<
  arrMyPrettySample[0]  "string 1234& абв "
  arrMyPrettySample[1]  function
>>>
function() {
  return a + b;
}<<<
  arrMyPrettySample[2]  >[object Object]<
    arrMyPrettySample[2].a  3.2
    arrMyPrettySample[2].b  "v4"
    arrMyPrettySample[2].c  Array[2]  >1,a<
      arrMyPrettySample[2].c[0]  1
      arrMyPrettySample[2].c[1]  "a"
    arrMyPrettySample[2].d  ""
  arrMyPrettySample.-1  -2
  arrMyPrettySample[3]  Array[4]  >98,false,function() {\r\n  x++;\r\n},[obj...<
    arrMyPrettySample[3][0]  98
    arrMyPrettySample[3][1]  false
    arrMyPrettySample[3][2]  function
>>>
function() {
  x++;
}<<<
    arrMyPrettySample[3][3]  >[object Object]<
      arrMyPrettySample[3][3].prop  "value"
  arrMyPrettySample[30]  >null<
  arrMyPrettySample[31]  NaN
  arrMyPrettySample[32]  undefined
  arrMyPrettySample[33]  Infinity
  arrMyPrettySample[55]  Wed Apr 20 00:31:34 UTC+0600 2011
  arrMyPrettySample[44]  /123/g
    arrMyPrettySample[44].lastIndex  0

Мне так гораздо больше нравится.

upd Небольшие правки и добавки.

36

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Опсс. Видимо придется еще делать сортировку элементов массивов, чтобы индексы шли по возрастанию.

37

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

if (oBj instanceof RegExp) { //В зависимости от браузера typeof то object, то function

typeof очень ненадежное средство для определения типа переданной переменной


typeof {} == 'object'
typeof null == 'object'
typeof [] == 'object'

instanceof гораздо лучше справляется с проблемой, но плохо работает в оконных документах с использованием фреймов (frame, iframe). Если Вы не планируете активно работать с такими документами и ограничиваетесь только одним документов или работаете в консольном JScript, то проблем не будет.

Можно еще проверять конструктор:


if ( object && object.contructor == RegExp )

Но у него те же самые проблемы. Если Вас не беспокоит эта проблема, то и ничего страшного. 

Есть универсальное средство:


if ( Object.prototype.toString.call(object) == '[object RegExp]' )
( 2 * b ) || ! ( 2 * b )

38

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

Rumata пишет:

typeof очень ненадежное средство для определения типа переданной переменной

Ну, для примеров {}, [] и null получать 'object' - это ж не баг, а вроде так и задумывалось. Что есть массив - как не объект? Что есть null - как не информация об отсутствии объекта? Так что я бы переформулировал "typeof не очень удобное средство определения типа".

Rumata пишет:

instanceof гораздо лучше справляется с проблемой, но плохо работает в оконных документах с использованием фреймов (frame, iframe)

Ага, прочел и осознал: тот же прототип, но из другого фрейма для instanceof уже другой прототип. Поэтому в общем случае надо переводить типы в строки по которым уже судить типах. Например, используя такую функцию:

/**
Источник https://github.com/kangax/protolicious/blob/master/experimental/__getClass.js
* Returns internal [[Class]] property of an object
* __getClass(5); // => "Number"
* __getClass({}); // => "Object"
* __getClass(/foo/); // => "RegExp"
* __getClass(''); // => "String"
* __getClass(true); // => "Boolean"
* __getClass([]); // => "Array"
* __getClass(undefined или null); // => "Window" или "global" или "DOMWindow" или "Object" в зависимости от браузера
//да-да снова полный разброд и шатание, так что метод в данной редакции не является "универсальным определителем типа" :(
* __getClass(Element); // => "Constructor"
*
*/
function __getClass(object){
  return Object.prototype.toString.call(object)
    .match(/^\[object\s(.*)\]$/)[1];
};

/*
И везде вместо 
объект instanceof НекийТип
использовать
__getClass(объект) == "НекийТип"
*/

О, дорогая редакция... (с)

Что ж, вот вариант, работающий и для фреймов тоже:

/** Возвращает строку рекурсивного описания содержимого объекта oBj
 * @param sParentName - имя объекта, произвольное, по сути комментарий (можно опустить)
 * @param nEsting - максимальный уровень вложенности объектов/значений (можно опустить)
 */
function vardump(oBj, sParentName, nEsting) {
  var sNL = "\r\n"; // Перенос строки 
  var nEsting_max = 12; //Максимальный исследуемый уровень вложенности, против бесконечных ссылок на самого себя
  var nPadding = 2; //Структурный отступ сдвига потомков относительно родителей, в пробелах
  //Ограничивать ли слишком длинное отображение объекта в поле >значения<
  var bRestrictObjectVal = true, nRestrictWidth = 80, sCutSign = "...";
  var bShowOnlyOwnProps = true; //Показывать только собственные (уникальные) свойства объекта или все приобретенные
  var bShowFunctionsCode = true; //Показывать ли код функций
  if (sParentName == undefined) sParentName = "";
  if (isNaN(parseInt(nEsting))) nEsting = nEsting_max;
  var sRes = "";
  var sGetClassOf = function(object) {
    return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
  };
  for ( var i = 0; i < nPadding * (nEsting_max - nEsting); i++)
    sRes += " ";
  if (nEsting >= 0) {
    sRes += sParentName + "  ";
    switch (typeof oBj) { //Описание элемента toString() и обрамление в соответствии с его типом 
    case "string":
      sRes += "\"" + oBj + "\"";
      break;
    case "function":
      if (sGetClassOf(oBj) == "RegExp") //В Chrome typeof RegExp => function
      sRes += oBj + "  as ";
      sRes += "function";
      break;
    case "object":
      if (sGetClassOf(oBj) == "Array") {
        sRes += "Array[" + oBj.length + "]  ";
      }
      if (sGetClassOf(oBj) == "Date" || sGetClassOf(oBj) == "RegExp") {
        sRes += oBj;
      }
      else { //Вывод прочих объектов и массивов
        sRes += ">";
        var sOTS = ("" + oBj).replace(/\r/g, "\\r").replace(/\n/g, "\\n");
        if (bRestrictObjectVal) { //Если надо, подрезаем слишком длинные описания в строку
          var nMaxOTSLength = nRestrictWidth - sRes.length - 1; //-1 под "<"
          var nCutOSTLength = nMaxOTSLength - sCutSign.length;
          if (nCutOSTLength < 0) nCutOSTLength = 0;
          if (sOTS.length > nMaxOTSLength) sOTS = sOTS.substr(0, nCutOSTLength) + sCutSign;
        }
        sRes += sOTS + "<";
      }
      break;
    default: //числа, спецзначения, логические
      sRes += oBj;
    }
    sRes += sNL;

    var sEl;
    var sGetChildName =
      function(oParent, sParentName, sChildName) {
        var sFullChildName = sParentName;
        var bIsIndex = false;
        if (sGetClassOf(oParent) == "Array") {
          var nIdx = parseInt(sChildName, 10);
          if (!isNaN(nIdx) && nIdx.toString() === sChildName.toString() && nIdx >= 0 && nIdx < oParent.length)
            bIsIndex = true;
        }
        if (bIsIndex) sFullChildName += "[" + sChildName + "]";
        else {
          if (sFullChildName != "") sFullChildName += "."; //Если есть что отделять, отделяем точкой
          sFullChildName += sChildName;
        }
        return sFullChildName;
      }; //sGetChildName

    if (sGetClassOf(oBj) == "RegExp") { //В зависимости от браузера typeof то object, то function
      sEl = "lastIndex";
      sRes += arguments.callee(oBj[sEl], sGetChildName(oBj, sParentName, sEl), nEsting - 1);
    }
    else { //Осмотрим внутренности
      switch (typeof oBj) {
      case "object": //Объекты и массивы
        for (sEl in oBj) {
          if (bShowOnlyOwnProps && !oBj.hasOwnProperty(sEl)) break;
          sRes += arguments.callee(oBj[sEl], sGetChildName(oBj, sParentName, sEl), nEsting - 1);
        }
        break;
      case "function":
        if (bShowFunctionsCode) sRes += ">>>" + sNL + oBj + "<<<" + sNL;
        break;
      //Остальное содержимого не имеет
      }
    }
  }
  else
    sRes += sParentName + " ! Nesting limit !" + sNL;
  return(sRes);
}

39

Re: JScript: Вывод свойств и значений массива или объекта - дополнение

typeof не очень удобное средство определения типа

не возражаю )))

var sGetClassOf = function(object)

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


...
      if (sGetClassOf(oBj) == "Array") {
...
      }
      if (sGetClassOf(oBj) == "Date" || sGetClassOf(oBj) == "RegExp") {

...
      var t = Object.prototype.toString.call(oBj);
      if (t == "[object Array]") {
...
      }
      if (t == "[object Date]" || t == "[object RegExp]") {
( 2 * b ) || ! ( 2 * b )