From 010387cc27f2864ca4fb00b811a178d191e13622 Mon Sep 17 00:00:00 2001 From: francesco Date: Tue, 11 Aug 2026 17:20:31 +0200 Subject: [PATCH] perf(isFloat): cache the compiled regex instead of rebuilding it per call isFloat built a new RegExp on every invocation. The pattern only varies with the decimal separator, which comes from the fixed set of locales, so the compiled regexes are now cached by separator. isInt already hoists its regexes to module scope; this brings isFloat in line. Behaviour is unchanged: the pattern is built from the same template literal, and the existing test suite passes. --- src/lib/isFloat.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/lib/isFloat.js b/src/lib/isFloat.js index 84bdc782c..01ce6c9f1 100644 --- a/src/lib/isFloat.js +++ b/src/lib/isFloat.js @@ -2,10 +2,25 @@ import assertString from './util/assertString'; import isNullOrUndefined from './util/nullUndefinedCheck'; import { decimal } from './alpha'; +// The pattern only varies with the decimal separator, which comes from a fixed set of +// locales, so the compiled regexes are cached instead of being rebuilt on every call. +const floatRegexByDecimal = new Map(); + +function floatRegex(decimalSeparator) { + let regex = floatRegexByDecimal.get(decimalSeparator); + + if (!regex) { + regex = new RegExp(`^(?:[-+])?(?:[0-9]+)?(?:\\${decimalSeparator}[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$`); + floatRegexByDecimal.set(decimalSeparator, regex); + } + + return regex; +} + export default function isFloat(str, options) { assertString(str); options = options || {}; - const float = new RegExp(`^(?:[-+])?(?:[0-9]+)?(?:\\${options.locale ? decimal[options.locale] : '.'}[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$`); + const float = floatRegex(options.locale ? decimal[options.locale] : '.'); if (str === '' || str === '.' || str === ',' || str === '-' || str === '+') { return false; }