1

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! :)

  • I copied this into a page and ran it a few times in a row and it ran successful for me every time. Is there something else happening possibly? Or could it be the same value isn't being passed each time? – spinon Jun 30 '10 at 19:02
  • I loaded the page and then in Firebug executed the line same line twice. Hmmm... odd. –  Jun 30 '10 at 19:04
  • Are you sure that this is the code that you are actually using? It's not logging the final score either of the times that you call the function, and that's not inside any condition, so it whould always do that before returning the result. – Guffa Jun 30 '10 at 19:16
  • I removed that line to avoid confusion. But yes, I am certain that that is the code. I copied over the results and then the code but before I copied the code over, I added that one line as a test. –  Jun 30 '10 at 19:33
  • possible duplicate of [Why RegExp with global flag in Javascript give wrong results?](http://stackoverflow.com/questions/1520800/why-regexp-with-global-flag-in-javascript-give-wrong-results) – Helen Jan 30 '11 at 14:42

1 Answers1

1

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

Community
  • 1
  • 1
Anurag
  • 140,337
  • 36
  • 221
  • 257
  • Ah, that explains it. I wonder how to get it to start at the beginning again. Thanks. –  Jun 30 '10 at 19:54