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