arguments — очень специфическая штука, о которой новички и даже любители знают только то, что это «вроде массив, но какой-то неправильный». На самом деле, у него есть ряд интересных особенностей. Предлагаю в топике пофантазировать на тему TypeHinting, аргументов по-умолчанию и всякого другого.(function (foo, bar) { console.log(typeof arguments); // ? arguments[0] = 42; console.log(foo); // ? })(10, 20);
А также покажу интересную идею-библиотеку
function test (foo, bar) { Args(arguments).defaults(100, 100); return [foo, bar]; }; test( ); // 100, 100 test(15 ); // 15, 100 test(21, 42); // 21, 42
В первую очередь хотел бы заметить, что множество идей высказанных в топике являются достаточно спорными. Я сам не уверен, что буду ими пользоваться и не советую пользоваться новичкам.
Что такое arguments
Это хэш. Обычный хэш, как
var object = {}(function () { console.log( typeof arguments, // object Object.getPrototypeOf(arguments) == Object.prototype // true ) })();
Сделать из него массив просто:
var array = Array.prototype.slice.call(arguments, 0); // или покороче, но менее производительно: var array = [].slice.call(arguments, 0);
Мы вызываем метод
slice прототипа Array от лица arguments.Что есть в arguments
arguments.length — количество аргументов, переданных в функцию.var count = function () { console.log(arguments.length); }; count(); // 0 count(first, second); // 2
Не забывайте, что у каждой функции тоже есть свойство
length, которое указывает на то, сколько элементов объявлено в её заголовке:function one (foo) {}; function three (foo, bar, qux) {}; console.log( one.length); // 1 console.log(three.length); // 3
arguments.callee — ссылка на саму функцию.function foo () { console.log(arguments.callee === foo); // true }
Таким образом можно проверить, передано ли правильное количество элементов, или нет:
function test (foo, bar, qux) { return arguments.callee.length === arguments.length; } test(1); // false test(1,2,3); // true
Аргументы в arguments
В
arguments содержится также список переданных аргументов.function test (foo, bar) { console.log(foo, bar); // 'a', 'b' console.log(arguments[0], arguments[1]); // 'a', 'b' } test('a', 'b');
Теперь к интересному. Многие не знают, что объект arguments — содержит на самом деле ссылки, а не значения, и тесно связан с аргументами:
(function (foo) { arguments[0] = 42; console.log(foo); // 42! foo = 20; console.log(arguments[0]); // 20 })(5);
При этом связь достаточно крепкая:
function foo (qux) { change(arguments); return qux; }; function change(a) { a[0] = 42; } foo(10); // 42
Что из этого можно получить?
Во многих языках программирования есть «переменные по-умолчанию». К примеру, php:
function ($foo = 30, $bar = 'test') { var_dump($foo); var_dump($bar); }
В javascript оно будет выглядеть как-то так:
function (foo, bar) { if (typeof foo === 'undefined') foo = 30; if (typeof bar === 'undefined') bar = 'test'; console.log(foo, bar); }
Зная особенности
arguments можно создать красивый интерфейс:function test(foo, bar) { Args(arguments).defaults(30, 'test'); console.log(foo, bar) } test(); // 30, 'test'
С помощью такого кода:
function Args (args) { if (this instanceof Args) { this.args = args; } else { // Если создано не через new, а просто вызвана функция, создаем и возвращаем новый объект return new Args(args); } }; Args.prototype = { defaults: function () { var defaults = arguments; for (var i = defaults.length; i--;) { if (typeof args[i] === 'undefined') args[i] = defaults[i]; } return this; } };
Аналогично можно сделать автоматическое приведение типов:
function test(foo) { Args(arguments) .defaults(10) .cast(Number); console.log(foo) } test('0100'); // 100
Или Type Hinting:
function test(foo, bar) { Args(arguments).types(Foo, Bar); // code } test(new Foo(), new Bar()); test(1, 2); // Error
Из интересных идей — сообщение, что все аргументы обязательны:
function test (foo, bar, qux) { Args(arguments).allRequired(); } test(1,2,3); // success test(1,2); // Error: 3 args required, 2 given
Заключение
Все эти идеи и возможности (и даже больше) я оформил в библиотеку — Args.js.
Согласен, что кое-какие вещи (как TypeHinting) не совсем подходят к идеологии языка. В то же время например defaults — очень удобная штука, имхо.
Пока что это прототип и, перед тем как вы будете его использовать — будьте уверены, что оно вам действительно нужно, а не что вы просто стараетесь из прекрасного языка сделать что-то похожее на C#.
Предлагаю обсудить, покритиковать код, найти пару багов и закоммитить несколько строк кода)
Args.js
К сожалению, из-за бага в трёх популярных браузерах(IE, Fx, Opera) я не смог добиться желаемого эффекта, полноценно самое вкусное заработало только в Chrome (ну по крайней мере в node.js работать будет)). Надеюсь, решим эту проблему вместе.
UPD: В комментах выяснили, что таки это бага Хрома, но, зато, какая приятная! Спасибо jamayka
