¿Cuál es la función más corta para leer una cookie por nombre en JavaScript?

193

¿Cuál es el método más corto, preciso y compatible con varios navegadores para leer una cookie en JavaScript?

Muy a menudo, al crear scripts independientes (donde no puedo tener dependencias externas), me encuentro agregando una función para leer cookies, y generalmente recurro alreadCookie() método QuirksMode.org (280 bytes, 216 minificados).

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;
}

Hace el trabajo, pero es feo, y agrega bastante hinchazón cada vez.

El método por el cual jQuery.cookie usa algo como esto (modificado, 165 bytes, 125 minificados):

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

Tenga en cuenta que esta no es una competencia de 'Código Golf': estoy legítimamente interesado en reducir el tamaño de mi función readCookie y en garantizar que la solución que tengo sea válida.

Yahel
fuente
8
Los datos de cookies almacenados son un poco feos, por lo que cualquier método para manejarlos probablemente también lo sea.
mVChr
44
@mVChr en serio. ¿En qué momento se decidió que se debía acceder a las cookies desde una cadena delimitada por punto y coma? ¿Cuándo fue una buena idea?
Yahel
77
¿Por qué esta pregunta sigue abierta y por qué tiene una recompensa? ¿Realmente estás tan desesperado por ahorrar quizás 5 bytes?
Mark Kahn
cookieArr = document.cookie.split (';'). map (ck => {return {[ck.split ('=') [0] .trim ()]: ck.split ('=') [1] }})
vladimir.gorea

Respuestas:

196

Más corto, más confiable y más eficaz que la respuesta actual mejor votada:

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

Aquí se muestra una comparación de rendimiento de varios enfoques:

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

Algunas notas sobre el enfoque:

El enfoque regex no solo es el más rápido en la mayoría de los navegadores, sino que también ofrece la función más corta. Además, debe señalarse que de acuerdo con la especificación oficial (RFC 2109) , el espacio después del punto y coma que separa las cookies en el documento.cookie es opcional y se podría argumentar que no se debe confiar en él. Además, se permite el espacio en blanco antes y después del signo igual (=) y se podría argumentar que este espacio en blanco potencial debe incluirse en cualquier analizador confiable de documentos. La expresión regular anterior representa las dos condiciones de espacio en blanco anteriores.

Mac
fuente
44
Acabo de notar que en Firefox, el enfoque de expresiones regulares que publiqué anteriormente no es tan eficaz como el enfoque de bucle. Las pruebas que había realizado anteriormente se realizaron en Chrome, donde el enfoque de expresiones regulares funcionó modestamente mejor que otros enfoques. No obstante, sigue siendo el más corto que aborda la pregunta que se hace.
Mac
66
Por que getCookieValue(a, b) toma el parámetro b?
Brent Washburne
15
Votado, pero no por legibilidad ... me tomó un tiempo descubrir qué ayb hacer.
Gigi
9
Inteligente, pero tonto para escribirlo de esa manera para ahorrar 1 byte.
The Muffin Man
55
aEl parámetro no se regex escapó, aunque puede ser útil, no es seguro. Cosas como getCookieValue('.*')devolverá cualquier cookie aleatoria
Vitim.us
185

Esto solo llegará a document.cookie UNA vez. Cada solicitud posterior será instantánea.

(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
})();

Me temo que no hay una forma más rápida que esta lógica general a menos que sea libre de usar .forEach que depende del navegador (incluso entonces no está ahorrando demasiado)

Su propio ejemplo ligeramente comprimido para 120 bytes:

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

Puede obtenerlo 110 bytessi lo convierte en un nombre de función de 1 letra, 90 bytessi suelta elencodeURIComponent .

Lo he entendido 73 bytes, pero para ser justos, es 82 bytescuando se nombra readCookiey 102 bytesluego agrega encodeURIComponent:

function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}
Mark Kahn
fuente
Olvidé la declaración de devolución, no hay nada malo con el cierre, sheesh.
Mark Kahn el
3
...¿Qué? Diff dice que lo hiciste. d.pr/sSte no tenía la ()llamada al final, por lo que estaba definiendo una función anónima pero nunca ejecutándola.
Yahel
2
Aquí tienes: jsperf.com/pre-increment-vs-post-increment Tenía razón, ++ i es más rápido, al menos si obtienes el valor antes y después. Y eso es lo que haces en un bucle for.
xavierm02
1
Una cosa importante: dado que el valor de "cookies" se almacena en caché, las cookies nuevas o modificadas de otras ventanas o pestañas no serán visibles. Puede evitar este problema almacenando el valor de cadena de document.cookie en una variable y verifique si no se modifica en cada acceso.
Andreas
2
@StijndeWitt: ¿cómo no responde la pregunta? ¿73 bytes no son lo suficientemente cortos para ti? :) La última respuesta que tengo es idéntica a la siguiente, a excepción de algunos controles de espacios en blanco, jajaja
Mark Kahn
20

Supuestos

Basado en la pregunta, creo que algunos supuestos / requisitos para esta función incluyen:

  • Se utilizará como una función de biblioteca y, por lo tanto, se debe colocar en cualquier base de código;
  • Como tal, tendrá que funcionar en muchos entornos diferentes , es decir, trabajar con código JS heredado, CMS de varios niveles de calidad, etc .;
  • Para interactuar con el código escrito por otras personas y / o código que usted no controla, la función no debe hacer ninguna suposición sobre cómo se codifican los nombres o valores de cookies . Llamar a la función con una cadena "foo:bar[0]"debería devolver una cookie (literalmente) llamada "foo: bar [0]";
  • Las cookies nuevas pueden escribirse y / o las cookies existentes pueden modificarse en cualquier momento durante la vida útil de la página.

Bajo estos supuestos, está claro que encodeURIComponent/ decodeURIComponent no debe usarse ; Al hacerlo, se supone que el código que establece la cookie también la codificó mediante estas funciones.

El enfoque de expresión regular se vuelve problemático si el nombre de la cookie puede contener caracteres especiales. jQuery.cookie soluciona este problema al codificar el nombre de la cookie (en realidad, tanto el nombre como el valor) al almacenar una cookie y decodificar el nombre al recuperar una cookie. Una solución de expresión regular está debajo.

A menos que solo esté leyendo las cookies que controla por completo, también sería recomendable leer las cookies document.cookiedirectamente y no almacenar en caché los resultados, ya que no hay forma de saber si la memoria caché no es válida sin document.cookievolver a leer .

(Si bien el acceso y el análisis document.cookiesserán un poco más lentos que el uso de un caché, no sería tan lento como leer otras partes del DOM, ya que las cookies no juegan un papel en los árboles DOM / render).


Función basada en bucle

Aquí va la respuesta de Code Golf, basada en la función PPK (basada en bucle):

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, '');
}

que cuando se minimiza, llega a 128 caracteres (sin contar el nombre de la función):

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,'');}

Función basada en expresiones regulares

Actualización: si realmente quieres una solución de expresión regular:

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

Esto escapa a cualquier carácter especial en el nombre de la cookie antes de construir el objeto RegExp. Minificado, esto llega a 134 caracteres (sin contar el nombre de la función):

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

Como Rudu y cwolves han señalado en los comentarios, la expresión regular de escape de expresión regular se puede acortar con algunos caracteres. Creo que sería bueno mantener la expresión regular de escape consistente (puede estar usándola en otro lugar), pero vale la pena considerar sus sugerencias.


Notas

Ambas funciones no manejarán nullo undefined, es decir, si hay una cookie llamada "nulo", readCookie(null)devolverá su valor. Si necesita manejar este caso, adapte el código en consecuencia.

Jeffery To
fuente
¿Cuál es el beneficio de #\sal final de su reemplazo-Regex? Ese reemplazo genérico se puede simplificar un poco más (-, no tiene sentido excepto entre paréntesis que se escapan): /[[\]{}()*+?.\\^$|]/g incluso se puede clasificar con encodeURIComponent: /[()*.\\]/g
Rudu
@Rudu Esa expresión regular de escape de expresiones regulares proviene de Simon Willison y también es utilizada por la biblioteca XRegExp . Está destinado al caso genérico (por ejemplo, si lo está haciendo, new RegExp('[' + str + ']')entonces querría escapar -), por lo que probablemente tenga razón en que puede acortarse. Como solo ahorraría unos pocos bytes, y escapar caracteres adicionales no afecta la expresión regular final, me inclino a dejarlo como está.
Jeffery Al
@Rudu También evitaría encodeURIComponent()en este caso porque creo que el OP está buscando una función genérica que pueda funcionar con cualquier nombre de cookie. Si el código de terceros establece una cookie llamada "foo: bar", usar encodeURIComponent()significará intentar leer una cookie llamada "foo% 3Abar".
Jeffery Al
Sé de dónde viene el código: no es perfecto (se convertirá a<tab>ben a\<tab>b... lo cual, aunque no es un escape válido \<space>). Y # parece no tener sentido en JS RegEx
Rudu
1
Una gran cantidad de bibliotecas de terceros usan encodeURIComponent, si se nombra una cookie foo=, se almacenaría como si foo%3Dno codificara el nombre de la misma manera que no lo encontrará (su código no lo encontrará): no hay una respuesta correcta. Si puede controlar cómo se colocan las cookies, puede simplificar la respuesta / hacer suposiciones para ayudar a sacarla. Lo más simple / mejor es usar solo a-zA-Z0-9 para el nombre de la cookie y evitar todo el encodeURIComponentdesastre de escape de RegExp. Pero es posible que deba preocuparse por decodeURIComponentel valor de las cookies, ya que es una práctica recomendada.
Rudu
14

código 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
}
ChicoDeFe
fuente
Cambié la última línea return d[0];y luego solía if (c('EXAMPLE_CK') == null)verificar si la cookie no está definida.
electroide
9

¿Que tal este?

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

Se contaron 89 bytes sin el nombre de la función.

Simon Steinberger
fuente
Respuesta inteligente Al punto. Merece más votos a favor. kpodría nombrarsekey por claridad, pero de todos modos.
GeroldBroser reintegra a Mónica el
Gracias :-) Por ahora, la respuesta aceptada se actualizó y también contiene esta. Pero en una forma aún un poco más comprimida con solo 73 caracteres.
Simon Steinberger
Entonces, estás apuntando a la brevedad, ya veo. ¿Por qué no está programando Puzzles y Code Golf en su lista de sitios entonces ... todavía? ¡Que te diviertas! Y, por cierto, yo también soy un escalador. Berg Heil! ;)
GeroldBroser reintegra a Mónica el
4

Aquí va ... ¡Salud!

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

Tenga en cuenta que hice uso de las cadenas de plantilla de ES6 para componer la expresión regex.

mfalade
fuente
3

esto en un objeto que puede leer, escribir, sobrescribir y eliminar 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;
    }
};

fuente
1

Ambas funciones parecen igualmente válidas en términos de lectura de cookies. Sin embargo, puede eliminar algunos bytes (y realmente está entrando en el territorio de Code Golf aquí):

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;
}

Todo lo que hice con esto es colapsar todas las declaraciones de variables en una declaración var, eliminar los segundos argumentos innecesarios en las llamadas a la subcadena y reemplazar la llamada charAt en una desreferencia de matriz.

Esto todavía no es tan corto como la segunda función que proporcionó, pero incluso eso puede tener algunos bytes eliminados:

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

Cambié la primera subexpresión en la expresión regular para que sea una subexpresión de captura, y cambié el resultado [1] parte a resultado [2] para coincidir con este cambio; También se eliminaron los parens innecesarios en torno al resultado [2].

Jeff Avallone
fuente
1

Para eliminar realmente la mayor hinchazón posible, considere no usar una función de envoltura:

try {
    var myCookie = document.cookie.match('(^|;) *myCookie=([^;]*)')[2]
} catch (_) {
    // handle missing cookie
}

Mientras esté familiarizado con RegEx, ese código es razonablemente limpio y fácil de leer.

Abhi Beckert
fuente
0

(editar: publicó la versión incorrecta primero ... y una no funcional en eso. Actualizada a actual, que utiliza una función de desempaquetado que es muy similar al segundo ejemplo).

Buena idea en el primer ejemplo cwolves. Construí ambos para una función de lectura / escritura de cookies bastante compacta que funciona en múltiples subdominios. Pensé que compartiría en caso de que alguien más se encuentre con este hilo en busca de eso.

(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 no pasa nada a rwCookie, obtendrá todas las cookies en el almacenamiento de cookies
  • Pasó a rwCookie un nombre de cookie, obtiene el valor de esa cookie del almacenamiento
  • Pasó un valor de cookie, escribe la cookie y coloca el valor en el almacenamiento
  • La caducidad predeterminada es sesión a menos que especifique uno
Adán
fuente
0

Usando la respuesta de cwolves, pero no usando un cierre ni un hash precalculado:

// 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];
  }
}

... y minificando ...

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];}}

... es igual a 127 bytes.

Félix Saparelli
fuente
0

Aquí está la solución más simple usando funciones de cadena javascript.

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

La siguiente función permitirá diferenciar entre cadenas vacías y cookies indefinidas. Las cookies no definidas volverán correctamente undefinedy no una cadena vacía a diferencia de algunas de las otras respuestas aquí. Pero no funcionará en IE7 y versiones posteriores, ya que no permiten el acceso a la matriz a los índices de cadena.

    function getCookie(name) {
      return (document.cookie.match('(?:^|;) *'+name+'=([^;]*)')||"")[1];
    }
Joyce Babu
fuente
¿Por qué devuelve el tercer elemento de la lista?
Nasch
Porque no hice que el primer grupo no capturara. He actualizado el código.
Joyce Babu