Комментарии 18
Мне не очень импонирует подход переопредения аргументов функций таким образом, чтобы одни и те же аргументы при различном их количестве менялись еще и местами.
Второй вариант (по моему мнению — легче работать с таким):
Это похоже на попытку в угоду удобству пожертвовать строгостью.
Опциональные аргументы все же должны идти в одном и том же порядке, а не меняться местами.
$.get(url, callback);
$.get(url, data, callback);
Второй вариант (по моему мнению — легче работать с таким):
$.get(url, callback);
$.get(url, callback, data);
Это похоже на попытку в угоду удобству пожертвовать строгостью.
Опциональные аргументы все же должны идти в одном и том же порядке, а не меняться местами.
А мне кажется, что callback в конце дает больше преимуществ.
1. Это снижает шансы не заметить дополнительные параметры при использовании анонимных callback'ов.
Параметр data видно сразу:
Параметр data видно уже после того, как прочитал тело callback'а:
2. Это удобно для частичного применения функций. В варианте с callback'ом в середине зафиксировать получится только аргументы до него.
3. Ну и наконец, это стандарт для Node.js и большого количества библиотек (тот же async, например).
1. Это снижает шансы не заметить дополнительные параметры при использовании анонимных callback'ов.
Параметр data видно сразу:
$.get(url, data, function (response) {
// …
})
Параметр data видно уже после того, как прочитал тело callback'а:
$.get(url, function (response) {
// …
}, data)
2. Это удобно для частичного применения функций. В варианте с callback'ом в середине зафиксировать получится только аргументы до него.
3. Ну и наконец, это стандарт для Node.js и большого количества библиотек (тот же async, например).
Нет нужды в магии или «языке определения параметров»
Это скорее минус, чем плюс. Если уж возникла ситуация когда необходимо написать функцию принимающую на вход различное кол-во аргументов, то я хочу быть уверен, что аргументы окажутся на своих местах, в зависимости от типов данных. Беглый поиск по модулям показал что rearg умеет это делать, обладая синтаксисом аналогичным vargs-callback.
Но я не понимаю проблемы передачи разного кол-ва аргументов. Можно взять за правило, что если не знаешь cколько аргументво нужно — все аргументы передавать в объекте:
func(params:Object, callback:Function)
Среди описанных способов нет моего любимого: передачи параметров в хэше (объекте):
Порядок аргументов произвольный. Названия параметров нельзя опустить, но этот факт существенно улучшает читаемость кода, так что я считаю эту обязаловку весомым плюсом.
К слову, такой подход используется в jQuery UI Widget Factory. Если писать виджеты с помощью этой библиотеки (4.5 КиБ с gzip, не считая jQuery), то самостоятельно оформлять обработку аргументов не требуется, можно сразу описывать логику.
var currentDoor = this.element;
openTheDoor({
door: currentDoor,
speed: 1000,
onFinish: function() {
enterTheDoor({ door: currentDoor });
}
});
Порядок аргументов произвольный. Названия параметров нельзя опустить, но этот факт существенно улучшает читаемость кода, так что я считаю эту обязаловку весомым плюсом.
К слову, такой подход используется в jQuery UI Widget Factory. Если писать виджеты с помощью этой библиотеки (4.5 КиБ с gzip, не считая jQuery), то самостоятельно оформлять обработку аргументов не требуется, можно сразу описывать логику.
Напоминает objectivec.
С грамотной подсветкой синтаксиса это выглядит еще круче.
Вот пример из IntelliJ IDEA / WebStorm с дефолтной темой Darcula:

Обратите внимание что блоки callback-функций визуально отделены, название аргумента на 40-й строке подсвечено: это означает, что вызов этого callback'а в функции не реализован.
Вот пример из IntelliJ IDEA / WebStorm с дефолтной темой Darcula:

Обратите внимание что блоки callback-функций визуально отделены, название аргумента на 40-й строке подсвечено: это означает, что вызов этого callback'а в функции не реализован.
Хороший подход для интерфейсных штук, когда callback'ов много и вся инфраструктура вокруг вашего кода работает по таким же соглашениям. Когда таких соглашений нет — придется самому все превращать в объекты. И с функциональными штуками вроде map и filter комбинируется только через промежуточные функции.
Статья не дает никаких ответов. Очевидно, что можно сдвинуть аргументы, так все и делают, это называется нормализация аргументов. Но автор даже в общеизвестной нормализации умудрился найти фатальный недостаток. В нормализации проблема всегда была только одна, вызов функций если не инлайнится, то замедляет код. (Пойду потестирую как сейчас с быстродействием нормализации вызовом функции)
В статье речь не об альтернативе нормализации, а о том, как эту нормализацию делать. «Очевидный сдвиг» аргументов в теле функции меня не устраивает, я рассказал почему.
Вам придется свыкнуться с нормализацией в теле функции:
Arguments normalization function vs inline
Arguments normalization function vs inline
Тест сферических присваиваний в вакууме, конечно, штука хорошая, но к практическому применению отношения имеет мало.
Весь сыр-бор ведь из-за функций, принимающих callback, верно? Так значит и тестировать нужно вызов с callback'ом. А то что вы протестировали — манипуляция массивами против присваивания переменных.
Тестировал я функции вот такого вида:
В процессе написания корректного тест-кейса я манипуляции массивами ускорил где-то в три с половиной раза.
Получилось вот что: Arguments normalization test
В Хроме этот тест выдает 2 047 549 Ops/sec VS 15 220 768 Ops/sec. Разница в восемь раз, а не в тысячу.
Насколько это критично? Зависит уже от абсолютного значения разницы.
А абсолютные цифры таковы: накладные расходы при однократном вызове декорированной функции составляют 0,4 микросекунды. Эта задержка не будет иметь никакого значения, если декорировать, например, jQuery.get или fs.readFile. До тех пор, конечно, пока вы не начнете вызывать их сотни тысяч раз в секунду.
Так что свыкаться с нормализацией в теле функции я причин не вижу. Задумываться о таких вещах, как инлайнинг, следует когда есть реальные проблемы с производительностью. Пока проблем нет, я лучше буду заботиться о чистоте и поддерживаемости кода.
«Premature optimization is the root of all evil», помните?
Весь сыр-бор ведь из-за функций, принимающих callback, верно? Так значит и тестировать нужно вызов с callback'ом. А то что вы протестировали — манипуляция массивами против присваивания переменных.
Тестировал я функции вот такого вида:
var add = function (a, b, options, callback) {
if (typeof options === 'function') {
callback = options
options = {}
}
var sum = a + b
return callback(null, sum)
}
В процессе написания корректного тест-кейса я манипуляции массивами ускорил где-то в три с половиной раза.
Получилось вот что: Arguments normalization test
В Хроме этот тест выдает 2 047 549 Ops/sec VS 15 220 768 Ops/sec. Разница в восемь раз, а не в тысячу.
Насколько это критично? Зависит уже от абсолютного значения разницы.
А абсолютные цифры таковы: накладные расходы при однократном вызове декорированной функции составляют 0,4 микросекунды. Эта задержка не будет иметь никакого значения, если декорировать, например, jQuery.get или fs.readFile. До тех пор, конечно, пока вы не начнете вызывать их сотни тысяч раз в секунду.
Так что свыкаться с нормализацией в теле функции я причин не вижу. Задумываться о таких вещах, как инлайнинг, следует когда есть реальные проблемы с производительностью. Пока проблем нет, я лучше буду заботиться о чистоте и поддерживаемости кода.
«Premature optimization is the root of all evil», помните?
Нормализация в теле действительно уродлива, но альтернативы нет. Если бы вашим методом можно было сделать одну универсальную функцию конструктора декорации, а то ведь нет, таких штук нам потребуется много.
Падение производительности не позволяет нам принять это декорирование как правило в кодегайд, то есть где-то придется писать так, где-то по требованиям производительности классически, что добавит неразберихи.
Где-то в коде нужна информация о том как нормализуются аргументы, либо комментарий писать, либо читаемое имя функции-конструктора.
Ваш пример читаемости кода не добавляет ничуть.
Попробуйте в файрфоксе это потестировать, там совсем все плохо.
Про оптимизацию категорически не согласен. С таким подходом можно даже не замечать что налетел на неоптимальность и все тормозит.
***
Но свое едкое замечание я заберу назад, ибо экспериментировать и что-то пытаться улучшить всегда нужно, тут я вас поддерживаю.
Падение производительности не позволяет нам принять это декорирование как правило в кодегайд, то есть где-то придется писать так, где-то по требованиям производительности классически, что добавит неразберихи.
Где-то в коде нужна информация о том как нормализуются аргументы, либо комментарий писать, либо читаемое имя функции-конструктора.
Ваш пример читаемости кода не добавляет ничуть.
Попробуйте в файрфоксе это потестировать, там совсем все плохо.
Про оптимизацию категорически не согласен. С таким подходом можно даже не замечать что налетел на неоптимальность и все тормозит.
***
Но свое едкое замечание я заберу назад, ибо экспериментировать и что-то пытаться улучшить всегда нужно, тут я вас поддерживаю.
А, ну да, тут и тестировать нечего. Функция с нормализацией через вызов другой функции сама никогда не будет инлайниться. То есть кривой способ с нормализацей аргументов вручную с нами навечно, ибо альтернатива зачастую в тысячу раз медленнее выполняется.
Почему бы просто не делать наподобие этого? Использование библиотек? о_О
gist.github.com/nooks/d0f7e4a2250c7e054931
gist.github.com/nooks/d0f7e4a2250c7e054931
Это по сути модификация стандартного бойлерплейта вида
Только модификация еще и мутноватая. Не сразу понятно, правильно ли там все работает из-за того, что используется состояние массива args. Уж лучше переприсваивать параметры.
Бибилиотека мне нужна для того, чтобы в каждой вариадической функции не писать одно и то же.
function (required, optional, callback) {
if (typeof optional === 'function') {
callback = optional
optional = 'default value'
}
// ...
}
Только модификация еще и мутноватая. Не сразу понятно, правильно ли там все работает из-за того, что используется состояние массива args. Уж лучше переприсваивать параметры.
Бибилиотека мне нужна для того, чтобы в каждой вариадической функции не писать одно и то же.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Переменное количество аргументов: проблемы и решения