4
\$\begingroup\$

Recently I needed to manipulate with cookies using JavaScript, so I wrote a class:

function Cookies() {
    var cookieLifeTime = null;

    this.setCookie = setCookie;
    this.getCookie = getCookie;
    this.deleteCookie = deleteCookie;
    this.setLifeTime = setLifeTime;

    function setLifeTime(lifeTime) {
        cookieLifeTime = lifeTime;
    }

    function setCookie(name, value) {
        if (this.lifeTime !== null) {
            var date = new Date();
            date.setTime((date.getTime() + cookieLifeTime));
            var expires = '; expires=' + date.toGMTString();
        } else {
            var expires = '';
        }

        document.cookie = name + '=' + value + expires + '; path=/';
    }

    function getCookie(name) {
        name += '=';
        var ca = document.cookie.split(';');

        for(var i=0; i < ca.length; i++) {
            var c = ca[i];

            while (c.charAt(0) === ' ')
                c = c.substring(1, c.length);

            if (c.indexOf(name) === 0)
                return c.substring(name.length, c.length); //Edited. Reduced not extra iterations
        }

        return null;
    }

    function deleteCookie(name) {
        setCookie(name, '', -1);
    }
}

I am not a JavaScript developer so maybe I've over-complicated things. Are there parts that can be optimized (I mean readability mostly)? I am also interested if there could be some edge-cases where this script will fail.

\$\endgroup\$
3
  • \$\begingroup\$ As far as I'm aware, there is already a plugin for it in the wild. Why not use it instead? \$\endgroup\$ Commented Mar 15, 2013 at 11:31
  • \$\begingroup\$ @JanDvorak Usually, I don't work with javascript. So I am not familiar with its plugins and implementing this class was not a big deal. That's why I went on my own. \$\endgroup\$
    – Leri
    Commented Mar 15, 2013 at 12:47
  • 1
    \$\begingroup\$ Your code seems very readable to me. It would pass my code review ;-) \$\endgroup\$ Commented Mar 15, 2013 at 13:09

2 Answers 2

4
\$\begingroup\$

As Dvorak said in the comments, your code is fine as-is. Still, I have some suggestions:

  1. I'd forego the *Cookie suffix on the function names. Seems too verbose, considering the constructor is called Cookies already. Only trick is that you can't have function called delete as that's a reserved word, but remove works fine too.

  2. I think you have a bug: In deleteCookie you call your own setCookie with a negative lifetime - but setCookie doesn't take a lifetime argument, so the cookie won't be deleted as expected.
    I'd fix this by adding such a 3rd argument to setCookie but making it optional:

    function setCookie(name, value, lifetime) {
      var date, expires = '';
      // default to cookieLifeTime if no arg was passed
      if( typeof lifetime !== 'undefined' ) {
        lifetime = cookieLifeTime);
      }
      if( lifetime ) {
        date = new Date();
        date.setTime(date.getTime() + lifetime);
        expires = '; expires=' + date.toGMTString()
      }    
      document.cookie = name + '=' + value + expires + '; path=/';
    }
    
  3. The getCookie can be made considerably shorter with a bit or regular expression magic.

    function getCookie(name) {
      var pattern = new RegExp(name + '=([^;]+)', ''),
          match = document.cookie.match(pattern);
      if(match) {
        return match[1];
      }
      return null;
    }
    
  4. As RoToRa says in another answer, a simple object literal might be neater, if you're willing to either skip the cookieLifeTime instance variable, or make it completely global. Here's how I'd probably write it:

    // lowercase, as it's no longer a constructor
    window.cookies = {
      set: function (name, value, lifetime) {
        var date, expires = '';
        // for mysterious reasons `NaN` is a number in JS,
        // so check for NaN too
        if( typeof lifetime === 'number' && !isNaN(lifetime) ) {
          date = new Date();
          date.setTime(date.getTime() + lifetime);
          expires = '; expires=' + date.toGMTString()
        }    
        document.cookies = name + '=' + value + expires + '; path=/';
      },
    
      get: function (name) {
        var pattern = new RegExp(name + '=([^;]+)', ''),
            match = document.cookie.match(pattern);
        if(match) {
          return match[1];
        }
        return null;
      },
    
      remove: function(name) {
        window.cookies.setCookie(name, '', -1);
      }
    };
    
\$\endgroup\$
2
  • \$\begingroup\$ +1 Nice review. Thanks for noticing bug. :) BTW, should not it be typeof lifetime === 'undefined'? \$\endgroup\$
    – Leri
    Commented Mar 15, 2013 at 14:39
  • \$\begingroup\$ @PLB you could do that, yes, but as typeof lifetime !== 'undefined' (note the negation). But checking for number is stricter, and perhaps better, since the var will be used for some math. However, I'm noticing a ton of typos in my code, so I'll be editing it a little :) \$\endgroup\$
    – Flambino
    Commented Mar 15, 2013 at 14:51
2
\$\begingroup\$

The only thing I'd suggest is that there is no real need for it being a class, since it's basically a singleton. So I'd use:

var Cookies = {
  // The same as your function
};
\$\endgroup\$
2
  • \$\begingroup\$ I was thinking the same thing, but it does have the cookieLifeTime "private" instance var \$\endgroup\$
    – Flambino
    Commented Mar 15, 2013 at 14:33
  • \$\begingroup\$ Right, I missed that. But it would make sense to turn the life time into a argument for setCookie anyway. \$\endgroup\$
    – RoToRa
    Commented Mar 15, 2013 at 14:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.