Как проверять regexp?
Вы делаете ошибку, тестируя только правильные варианты. Например, ваша регулярка посчитает правильными: function (, ,) или function(a b). И вы зря не обращаете внимания на примеры в интернете: \w короче и удобнее, чем [a-z0-9_]. Главное же - просто опыт.

Например, зачем повторять \(...\) в каждой группе, если это можно вынести за группировку? И зачем выделять отсутствие параметров в отдельный случай?

Я бы записал в таком виде:
\bfunction\s*(\w+)?\s*\(\s*(\w+(?:\s*,\s*\w+)*)?\s*\)
Но сама по себе идея анализировать язык программирования регулярными выражениями не является удачной:
регулярные выражения реализуют только регулярные грамматики (грамматики типа 3), тогда как для описания синтаксиса языка программирования требуется контекстно-свободная грамматика (грамматика типа 2). Разница между ними в том, что контекстно-свободная грамматика позволяет задавать рекурсивные правила (function внутри function) с неограниченной глубиной рекурсии.
Вы делаете ошибку, тестируя только правильные варианты. Например, ваша регулярка посчитает правильными: function (, ,) или function(a b). И вы зря не обращаете внимания на примеры в интернете: \w короче и удобнее, чем [a-z0-9_]. Главное же - просто опыт.

Например, зачем повторять \(...\) в каждой группе, если это можно вынести за группировку? И зачем выделять отсутствие параметров в отдельный случай?

Я бы записал в таком виде:
\bfunction\s*(\w+)?\s*\(\s*(\w+(?:\s*,\s*\w+)*)?\s*\)
Но сама по себе идея анализировать язык программирования регулярными выражениями не является удачной:
регулярные выражения реализуют только регулярные грамматики (грамматики типа 3), тогда как для описания синтаксиса языка программирования требуется контекстно-свободная грамматика (грамматика типа 2). Разница между ними в том, что контекстно-свободная грамматика позволяет задавать рекурсивные правила (function внутри function) с неограниченной глубиной рекурсии.