Quelle est la fonction la plus courte pour lire un cookie par nom en JavaScript?

Quelle est la méthode la plus courte, la plus précise et la plus compatible avec les navigateurs pour lire un cookie en JavaScript?

Très souvent, lors de la construction de scripts autonomes (où je ne peux pas avoir de dépendances extérieures), je me trouve en ajoutant une fonction pour lire des cookies et, en règle générale, en arrière sur la méthode readCookie() (280 octets, 216 Minifié.)

 function readCookie(name) { var nameEQ = 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(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } return null; } 

Il fait le travail, mais il est laid, et ajoute un peu de bêtises à chaque fois.

La méthode que jQuery.cookie utilise comme ceci (modifié, 165 octets, 125 minifié):

 function read_cookie(key) { var result; return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? (result[1]) : null; } 

Notez que ce n'est pas un concours «Code Golf»: je suis légitimement intéressé à réduire la taille de ma fonction readCookie et à assurer la solution que je possède est valide.

Cela n'atteindra jamais document.cookie UNE fois. Toute demande ultérieure sera instantanée.

 (function(){ var cookies; function readCookie(name,c,C,i){ if(cookies){ return cookies[name]; } c = document.cookie.split('; '); cookies = {}; for(i=c.length-1; i>=0; i--){ C = c[i].split('='); cookies[C[0]] = C[1]; } return cookies[name]; } window.readCookie = readCookie; // or expose it however you want })(); 

Je crains qu'il n'y ait pas de manière plus rapide que cette logique générale, sauf si vous êtes libre d'utiliser. .forEach ce qui dépend du navigateur (même si vous ne l'enregistrez pas)

Votre propre exemple légèrement compressé à 120 bytes :

 function read_cookie(k,r){return(r=RegExp('(^|; )'+encodeURIComponent(k)+'=([^;]*)').exec(document.cookie))?r[2]:null;} 

Vous pouvez l'obtenir à 110 bytes si vous en faites un nom de fonction de 1 lettre, 90 bytes si vous déposez le encodeURIComponent .

Je l'ai réduit à 73 bytes , mais pour être juste, il est de 82 bytes quand on l' readCookie et 102 bytes lors de l'ajout de encodeURIComponent :

 function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]} 

Plus court, plus fiable et plus performant que la réponse la mieux votée:

 function getCookieValue(a) { var b = document.cookie.match('(^|;)\\s*' + a + '\\s*=\\s*([^;]+)'); return b ? b.pop() : ''; } 

Une comparaison de performance de différentes approches est présentée ici:

http://jsperf.com/get-cookie-value-regex-vs-array-functions

Quelques notes sur l'approche:

L'approche regex n'est pas seulement la plus rapide dans la plupart des navigateurs, mais elle produit aussi la fonction la plus courte. En outre, il convient de souligner que selon la spécification officielle (RFC 2109) , l'espace après le point-virgule qui sépare les cookies dans le document.cookie est facultatif et un argument peut être fait qu'il ne doit pas être invoqué. De plus, les espaces blancs sont autorisés avant et après le signe égal (=) et un argument pourrait être fait que cet espace blanc potentiel devrait être pris en compte dans n'importe quel document documentaire .cookie fiable. Le regex ci-dessus représente les deux conditions ci-dessus.

Hypothèses

Sur la base de la question, je crois que certaines hypothèses / exigences pour cette fonction comprennent:

  • Il sera utilisé comme une fonction de bibliothèque , et devrait donc être jeté dans n'importe quelle base de code;
  • En tant que tel, il devra travailler dans de nombreux environnements différents , c'est-à-dire travailler avec un code JS hérité, des CMS de différents niveaux de qualité, etc.
  • Pour interagir avec un code écrit par d'autres personnes et / ou un code que vous ne contrôlez pas, la fonction ne doit pas faire d'hypothèses sur la façon dont les noms de cookies ou les valeurs sont encodées . Appeler la fonction avec une chaîne "foo:bar[0]" doit renvoyer un cookie (littéralement) nommé "foo: bar [0]";
  • Les nouveaux cookies peuvent être écrits et / ou des cookies existants modifiés à tout moment pendant la durée de la page.

Selon ces hypothèses, il est clair que l' encodeURIComponent / decodeURIComponent ne doit pas être utilisé ; Cela suppose que le code qui a configuré le cookie l'a également codé en utilisant ces fonctions.

L'approche d'expression régulière devient problématique si le nom de cookie peut contenir des caractères spéciaux. JQuery.cookie fonctionne autour de ce problème en codant le nom du cookie (en fait le nom et la valeur) lors du stockage d'un cookie et le décodage du nom lors de la récupération d'un cookie. Une solution d'expression régulière est ci-dessous.

Sauf si vous ne lisez que les cookies que vous contrôlez complètement, il serait également conseillé de lire les cookies à partir de document.cookie directement et ne cache pas les résultats car il n'y a aucun moyen de savoir si le cache n'est pas valide sans lire document.cookie nouveau.

(En accédant et en analysant document.cookies sera légèrement plus lent que l'utilisation d'un cache, il ne serait pas aussi lent que de lire d'autres parties du DOM, car les cookies ne jouent pas un rôle dans les arbres DOM / render.)


Fonction basée sur la boucle

Voici la réponse Code Golf, basée sur la fonction PPK (en boucle):

 function readCookie(name) { name += '='; for (var ca = document.cookie.split(/;\s*/), i = ca.length - 1; i >= 0; i--) if (!ca[i].indexOf(name)) return ca[i].replace(name, ''); } 

Qui, lorsqu'il est minifié, arrive à 128 caractères (sans compter le nom de la fonction):

 function readCookie(n){n+='=';for(var a=document.cookie.split(/;\s*/),i=a.length-1;i>=0;i--)if(!a[i].indexOf(n))return a[i].replace(n,'');} 

Fonction basée sur l'expression régulière

Mise à jour: si vous voulez vraiment une solution d'expression régulière:

 function readCookie(name) { return (name = new RegExp('(?:^|;\\s*)' + ('' + name).replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') + '=([^;]*)').exec(document.cookie)) && name[1]; } 

Cela échappe à tous les caractères spéciaux du nom de cookie avant de construire l'objet RegExp. Minimisé, il s'agit de 134 caractères (sans compter le nom de la fonction):

 function readCookie(n){return(n=new RegExp('(?:^|;\\s*)'+(''+n).replace(/[-[\]{}()*+?.,\\^$|#\s]/g,'\\$&')+'=([^;]*)').exec(document.cookie))&&n[1];} 

Comme Rudu et les loups ont souligné dans les commentaires, la régge d'expression régulière peut être raccourcie par quelques personnages. Je pense qu'il serait judicieux de conserver la cohérence d'échappement (vous pouvez l'utiliser ailleurs), mais leurs suggestions méritent d'être envisagées.


Remarques

Ces deux fonctions ne gèreront null ou undefined , c'est-à-dire s'il y a un cookie nommé "null", readCookie(null) renverra sa valeur. Si vous devez gérer cette affaire, adaptez le code en conséquence.

Code de google analytics ga.js

 function c(a){ var d=[], e=document.cookie.split(";"); a=RegExp("^\\s*"+a+"=\\s*(.*?)\\s*$"); for(var b=0;b<e.length;b++){ var f=e[b].match(a); f&&d.push(f[1]) } return d } 

Celui-ci, ça va?

 function getCookie(k){var v=document.cookie.match('(^|;) ?'+k+'=([^;]*)(;|$)');return v?v[2]:null} 

Conté de 89 octets sans le nom de la fonction.

Ces deux fonctions semblent également valables en termes de lecture de cookies. Vous pouvez vous débarrasser de quelques octets (et cela arrive vraiment dans le domaine Code Golf ici):

 function readCookie(name) { var nameEQ = name + "=", ca = document.cookie.split(';'), i = 0, c; for(;i < ca.length;i++) { c = ca[i]; while (c[0]==' ') c = c.substring(1); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length); } return null; } 

Tout ce que j'ai fait avec cela est d'effondrer toutes les déclarations de variables dans une instruction var, a supprimé les deuxièmes arguments inutiles dans les appels à la sous-chaîne, et de remplacer l'appel charAt dans une déréférence de tableau.

Ce n'est toujours pas aussi court que la deuxième fonction que vous avez fournie, mais même cela peut avoir quelques octets enlevés:

 function read_cookie(key) { var result; return (result = new RegExp('(^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? result[2] : null; } 

J'ai changé la première sous-expression dans l'expression régulière pour être une sous-expression de capture, et j'ai changé la partie résultat [1] en résultat [2] pour coïncider avec cette modification; A également supprimé les parens inutiles autour du résultat [2].

Ceci dans un objet que vous pouvez lire, écrire, écraser et supprimer des cookies.

 var cookie = { write : function (cname, cvalue, exdays) { var d = new Date(); d.setTime(d.getTime() + (exdays*24*60*60*1000)); var expires = "expires="+d.toUTCString(); document.cookie = cname + "=" + cvalue + "; " + expires; }, read : function (name) { if (document.cookie.indexOf(name) > -1) { return document.cookie.split(name)[1].split("; ")[0].substr(1) } else { return ""; } }, delete : function (cname) { var d = new Date(); d.setTime(d.getTime() - 1000); var expires = "expires="+d.toUTCString(); document.cookie = cname + "=; " + expires; } }; 

Voilà … Cheers!

 function getCookie(n) { let a = `; ${document.cookie}`.match(`;\\s*${n}=([^;]+)`); return a ? a[1] : ''; } 

Notez que j'ai utilisé les chaînes de modèles ES6 pour composer l'expression regex.

(Éditer: a posté la première version incorrecte … et une fonction non fonctionnelle à cela. Mise à jour en cours, qui utilise une fonction unparam qui ressemble beaucoup au deuxième exemple.)

Belle idée dans le premier exemple de loups. J'ai construit sur les deux pour une fonction de lecture / écriture de cookie assez compacte qui fonctionne sur plusieurs sous-domaines. J'ai trouvé que je partagerais si quelqu'un d'autre traverse ce fil à la recherche de cela.

 (function(s){ s.strToObj = function (x,splitter) { for ( var y = {},p,a = x.split (splitter),L = a.length;L;) { p = a[ --L].split ('='); y[p[0]] = p[1] } return y }; s.rwCookie = function (n,v,e) { var d=document, c= s.cookies||s.strToObj(d.cookie,'; '), h=location.hostname, domain; if(v){ domain = h.slice(h.lastIndexOf('.',(h.lastIndexOf('.')-1))+1); d.cookie = n + '=' + (c[n]=v) + (e ? '; expires=' + e : '') + '; domain=.' + domain + '; path=/' } return c[n]||c }; })(some_global_namespace) 
  • Si vous passez rwCookie rien, il obtiendra tous les cookies dans le stockage de cookies
  • Passé rwCookie un nom de cookie, il obtient la valeur de ce cookie depuis le stockage
  • Passé une valeur de cookie, il écrit le cookie et place la valeur dans le stockage
  • Expiration par défaut pour la session sauf si vous spécifiez un

En utilisant la réponse des loups, mais pas en utilisant une fermeture ni un hash pré-calculé:

 // Golfed it a bit, too... function readCookie(n){ var c = document.cookie.split('; '), i = c.length, C; for(; i>0; i--){ C = c[i].split('='); if(C[0] == n) return C[1]; } } 

… et minifiant …

 function readCookie(n){var c=document.cookie.split('; '),i=c.length,C;for(;i>0;i--){C=c[i].split('=');if(C[0]==n)return C[1];}} 

… équivaut à 127 octets.

Voici la solution la plus simple en utilisant les fonctions de chaîne javascript.

 document.cookie.substring(document.cookie.indexOf("COOKIE_NAME"), document.cookie.indexOf(";", document.cookie.indexOf("COOKIE_NAME"))). substr(COOKIE_NAME.length);