Reputation:
I wrote this validation method but am having issues with it.
function validate_password(pwd)
{
var score = 0;
// Min length is 8
if (pwd.length<8)
return false;
// Is lower present?
if (/[a-z]/g.test(pwd))
{
console.log('a-z test on "'+pwd+'":' + /[a-z]+/g.test(pwd));
score++;
}
// Is upper present?
if (/[A-Z]/g.test(pwd))
{
console.log('A-Z test on: "'+pwd+'":' + /[A-Z]+/g.test(pwd));
score++;
}
// Is digit present?
if (/\d/g.test(pwd))
{
console.log('digit test on: "'+pwd+'":' + /\d/g.test(pwd));
score++;
}
// Is special char present?
if (/\W/g.test(pwd))
{
console.log('spec char test on: "'+pwd+'":' + /\W/g.test(pwd));
score++;
}
if (score>=3)
return true;
else
return false;
}
This is what is written to the console:
>>> validate_password('aaasdfF#3s')
a-z test on "aaasdfF#3s":true
A-Z test on: "aaasdfF#3s":true
digit test on: "aaasdfF#3s":true
spec char test on: "aaasdfF#3s":true
true
>>> validate_password('aaasdfF#3s')
a-z test on "aaasdfF#3s":true
false
On the first try it seems to work as expected but when I call the method a 2nd time, it doesn't work as expected.
So, my question is why are there differences between the results from the first try and the 2nd try?
Thanks! :)
Upvotes: 1
Views: 228
Reputation: 141869
See MDC documentation on test
.
When you want to know whether a pattern is found in a string use the test method (similar to the String.search method); for more information (but slower execution) use the exec method (similar to the String.match method). As with exec, test called multiple times on the same regular expression instance will advance past the previous match.
The solution is to remove the global or g
flag from your regexes:
/[a-z]/
instead of /[a-z]/g
, and so on.
Consider this simple example to see why the problem exists:
var l = /[a-z]/g;
// initial search starts at the beginning, matches "a" and returns true
l.test("a"); // true
// since the first character matched, lastIndex moves to the next index - 1
l.lastIndex; // 1
// this time we pass a different string to the regex, but unfortunatly it
// starts searching from the lastIndex which is 1. There are no lower case
// letters from this point onwards (only "---"), so return value is false.
l.test("x---"); // false
// Since this search failed, lastIndex wraps around to the beginning, so the
// next search will work as expected
l.lastIndex; // 0
For your given input "aaasdfF#3s"
, the lower case [a-z]
test would have succeeded 7 times as there are 7 lower case characters, but fail the 8th time. And again succeed from the 9th to 15th time, and so on. The other tests will fail every alternate time as there is only one of each type of character - "F"
, "#"
, and "3"
and it wraps around lastIndex
to 0 when the test fails.
The problem seems to be stemming from the fact that state is preserved in those RegExp objects between function calls, meaning a RegExp object is only created once, and not every time the function is called. This little test confirms it:
function RegExpStatePersistenceTest() {
var regex = /[a-z]/g;
regex.counter = regex.counter || 0;
regex.counter++;
console.log("counter:" + regex.counter);
}
RegExpStatePersistenceTest(); // counter: 1
RegExpStatePersistenceTest(); // counter: 2
RegExpStatePersistenceTest(); // counter: 3
RegExpStatePersistenceTest(); // counter: 4
If a new object was explicitly created using new RegExp(..)
, then on each invocation of the function, a fresh new RegExp object will be created, and state will not be preserved between calls.
Also see, why-regexp-with-global-flag-in-javascript-give-wrong-results
Upvotes: 1