Тема: JScript: Расширение объекта Enumerator, упрощение работы с коллекциями
Предлагаю тему для обсуждения. Однажды класс уже вскользь упоминался на форуме - #11. Предлагаемое обсуждение немного перекликается с темой WSH: Сравнение VBScript и JScript
JScript, в отличие от VBScript, не имеет простых языковых средств перебора элементов коллекций. Чтобы решить эту проблему изобретатели из Рэдмонда ввели объект Enumerator
The Enumerator object provides a way to access any member of a collection and behaves similarly to the For...Each statement in VBScript.
Но Enumerator проигрывает в лаконичности For Each: много лишних переменных, громоздкая конструкция:
.
Сокращенный пример JScript из документации
var fso = new ActiveXObject("Scripting.FileSystemObject");
for (var e = new Enumerator(fso.Drives); ! e.atEnd(); e.moveNext()) {
var drive = e.item();
...
}
.
Вариант примера на VBScript
Set fso = CreateObject("Scripting.FileSystemObject")
For Each drive In fso.Drives
...
Next
.
Вместо этого предлагаю чуть более лаконичную конструкцию - по сути, "синтаксический сахар", полностью реализованный в духе JavaScript. Встроенный объект Enumerator расширен тремя однотипными статическими методами для обхода коллекций, которые, в зависимости от своей функции, генерируют массив, хэш (объект) или просто перебирают элементы коллекции. Методы по-прежнему используют встроенный объект Enumerator, но реализация полностью сокрыта внутри методов, текущий контекст свободен от избыточных переменных, сохранена лаконичность кода. Сравните с предыдущими примерами:
var fso = new ActiveXObject("Scripting.FileSystemObject");
Enumerator.forItems(fso.Drives, function(drive)
...
})
.
Enumerator.forItems принимает два аргумента:
-- коллекцию, элементы которой необходимо перебрать;
-- функцию, которая вызывается для каждого элемента коллекции. Аргументами функции являются текущий элемент коллекции и ссылка на коллекцию. В большинстве ситуацию достаточно указания одного аргумента.
Метод не возвращает результата.
Enumerator.toArray преобразует коллекцию и возвращает массив.
Enumerator.toObject преобразует коллекцию и возвращает объект.
Эти методы избыточны, так как их функциональность полностью реализуема с помощью Enumerate.forItems. Однако они могут быть полезны в силу их интуитивной ясности в контексте использования.
// Преобразовать коллекцию безымянных аргументов командной строки в массив
// Каждый аргумент доступен по его индексу, например, unnamed[0] - первый элемент
var unnamed = Enumerator.toArray(WScript.Arguments.Unnamed, function(value)
{
return value;
});
// Преобразовать коллецию именованых аргументов командой строки
// вида /ключ[:значение] в объект вида {"ключ": "значение"}
// "значение" программно доступно как named["ключ"]
var named = Enumerator.toObject(WScript.Arguments.Named, function(key, collection)
{
return {
key: key,
value: collection.item(key)
};
});
.
Далее - исходный код расширения. Полная и актуальная версия доступна по адресу http://code.google.com/p/jsxt/source/br … merator.js
Enumerator.toArray = function(collection, fun, thisp)
{
if ( typeof fun != "function" ) {
throw new TypeError();
}
var result = [];
var fc = new Enumerator(collection);
for ( ; ! fc.atEnd(); fc.moveNext()) {
var i = fc.item();
result.push(fun.call(thisp, i, collection));
}
return result;
};
Enumerator.toObject = function(collection, fun, thisp)
{
if ( typeof fun != "function" ) {
throw new TypeError();
}
var result = {};
var fc = new Enumerator(collection);
for ( ; ! fc.atEnd(); fc.moveNext()) {
var i = fc.item();
var r = fun.call(thisp, i, collection);
result[r.key] = r.value;
}
return result;
};
Enumerator.forItems = function(collection, fun, thisp)
{
if ( typeof fun != "function" ) {
throw new TypeError();
}
var fc = new Enumerator(collection);
for ( ; ! fc.atEnd(); fc.moveNext()) {
var i = fc.item();
fun.call(thisp, i, collection);
}
};