Qual è la funzione più breve per leggere un cookie per nome in JavaScript?


194

Qual è il metodo più breve, preciso e compatibile con più browser per leggere un cookie in JavaScript?

Molto spesso, durante la creazione di script autonomi (dove non posso avere dipendenze esterne), mi ritrovo ad aggiungere una funzione per la lettura dei cookie e di solito ripiego sulreadCookie() metodo QuirksMode.org (280 byte, 216 minimizzati).

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

Fa il lavoro, ma è brutto, e aggiunge un po 'di gonfiore ogni volta.

Il metodo che jQuery.cookie utilizza qualcosa del genere (modificato, 165 byte, 125 minimizzato):

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

Nota che questa non è una competizione 'Code Golf': sono legittimamente interessato a ridurre le dimensioni della mia funzione readCookie e ad assicurare che la soluzione che ho sia valida.


8
I dati sui cookie memorizzati sono piuttosto brutti, quindi probabilmente lo sarà anche qualsiasi metodo per gestirli.
mVChr,

4
@mVChr sul serio. A che punto è stato deciso di accedere ai cookie da una stringa delimitata da punti e virgola? Quando mai è stata una buona idea?
Yahel,

7
Perché questa domanda è ancora aperta e perché ha una taglia? Sei davvero così disperato per salvare forse 5 byte ???
Mark Kahn,

cookieArr = document.cookie.split (';'). map (ck => {return {[ck.split ('=') [0] .trim ()]: ck.split ('=') [1] }})
vladimir.gorea il

Risposte:


198

Più breve, più affidabile e più performante dell'attuale risposta più votata:

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

Un confronto delle prestazioni di vari approcci è mostrato qui:

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

Alcune note sull'approccio:

L'approccio regex non è solo il più veloce nella maggior parte dei browser, ma offre anche la funzione più breve. Inoltre, va sottolineato che, secondo le specifiche ufficiali (RFC 2109) , lo spazio dopo il punto e virgola che separa i cookie nel document.cookie è facoltativo e si potrebbe sostenere che non si dovrebbe fare affidamento su di esso. Inoltre, gli spazi bianchi sono consentiti prima e dopo il segno di uguale (=) e si potrebbe sostenere che questo potenziale spazio bianco dovrebbe essere preso in considerazione in qualsiasi parser document.cookie affidabile. La regex sopra spiega entrambe le condizioni di spazio bianco sopra.


4
Ho appena notato che in Firefox l'approccio regex che ho pubblicato sopra non è altrettanto efficace dell'approccio loop. I test che avevo eseguito in precedenza erano stati condotti su Chrome, dove l'approccio regex ha funzionato modestamente meglio di altri approcci. Tuttavia, è ancora il più breve a rispondere alla domanda che viene posta.
Mac,

6
Perché accetta getCookieValue(a, b)il parametro b?
Brent Washburne,

15
Voto, ma non per leggibilità ... mi ci è voluto un po 'per capire cosa ae cosa bfare.
Gigi,

9
Intelligente, ma sciocco per scriverlo in quel modo per salvare 1 byte.
The Muffin Man,

5
ail parametro non è regex escape, mentre può essere utile, non è sicuro. Cose come getCookieValue('.*')restituiranno qualsiasi cookie casuale
Vitim.us

185

Questo avrà sempre successo solo document.cookie UNA VOLTA. Ogni richiesta successiva sarà istantanea.

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

Temo che non ci sia davvero un modo più veloce di questa logica generale a meno che tu non sia libero di usare .forEachche dipende dal browser (anche se non stai risparmiando così tanto)

Il tuo esempio leggermente compresso per 120 bytes:

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

Puoi ottenerlo 110 bytesse lo rendi un nome di funzione di 1 lettera, 90 bytesse lasci cadere encodeURIComponent.

Ho capito bene 73 bytes, ma per essere onesti è 82 bytesquando viene nominato readCookiee 102 bytesquando si aggiunge encodeURIComponent:

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

Ho dimenticato la dichiarazione di ritorno, non c'è niente di sbagliato nella chiusura, sheesh.
Mark Kahn,

3
...Che cosa? diff dice che l'hai fatto. d.pr/sSte alla fine non aveva la ()chiamata, quindi stava definendo una funzione anonima ma non la eseguiva mai.
Yahel,

2
Ecco qui: jsperf.com/pre-increment-vs-post-increment Avevo ragione, ++ i è più veloce, almeno se ottieni il valore prima e dopo. Ed è quello che fai in un ciclo for.
xavierm02,

1
Una cosa importante: poiché il valore di "cookie" è memorizzato nella cache, i nuovi cookie o i cookie modificati da altre finestre o schede non saranno visibili. È possibile evitare questo problema memorizzando il valore di stringa da document.cookie in una variabile e verificando che sia invariato su ciascun accesso.
Andreas,

2
@StijndeWitt - come non risponde alla domanda? 73 byte non sono abbastanza brevi per te? :) L'ultima risposta che ho è identica a quella qui sotto, fatta eccezione per alcuni controlli sugli spazi bianchi, lol
Mark Kahn,

20

ipotesi

Sulla base della domanda, credo che alcuni presupposti / requisiti per questa funzione includano:

  • Verrà utilizzato come una funzione di libreria e quindi destinato a essere rilasciato in qualsiasi base di codice;
  • Come tale, dovrà funzionare in molti ambienti diversi , ovvero lavorare con codice JS legacy, CMS di vari livelli di qualità, ecc .;
  • Per interagire con il codice scritto da altre persone e / o codice che non si controlla, la funzione non dovrebbe fare ipotesi su come vengono codificati i nomi o i valori dei cookie . Chiamare la funzione con una stringa "foo:bar[0]"dovrebbe restituire un cookie (letteralmente) chiamato "foo: bar [0]";
  • Nuovi cookie possono essere scritti e / o cookie esistenti modificati in qualsiasi momento durante la vita della pagina.

Sotto questi presupposti, è chiaro che encodeURIComponent/ decodeURIComponent non dovrebbe essere usato ; in tal modo si presuppone che il codice che ha impostato il cookie lo abbia anche codificato utilizzando queste funzioni.

L'approccio all'espressione regolare diventa problematico se il nome del cookie può contenere caratteri speciali. jQuery.cookie risolve questo problema codificando il nome del cookie (in realtà sia nome che valore) durante la memorizzazione di un cookie e decodificando il nome durante il recupero di un cookie. Una soluzione di espressione regolare è di seguito.

A meno che tu non stia leggendo solo i cookie che controlli completamente, sarebbe anche consigliabile leggere i cookie document.cookiedirettamente e non memorizzare nella cache i risultati, poiché non è possibile sapere se la cache non è valida senza rileggerla document.cookie.

(Mentre l'accesso e l'analisi document.cookiessaranno leggermente più lenti rispetto all'utilizzo di una cache, non sarebbe così lento come la lettura di altre parti del DOM, poiché i cookie non svolgono un ruolo negli alberi DOM / render.)


Funzione basata su loop

Ecco la risposta di Code Golf, basata sulla funzione (basata su loop) di PPK:

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

che una volta minimizzato, arriva a 128 caratteri (senza contare il nome della funzione):

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

Funzione basata su espressioni regolari

Aggiornamento: se vuoi davvero una soluzione di espressione regolare:

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

Ciò sfugge a tutti i caratteri speciali nel nome del cookie prima di costruire l'oggetto RegExp. Minimizzato, arriva a 134 caratteri (senza contare il nome della funzione):

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

Come Rudu e i lupi hanno sottolineato nei commenti, la regex che sfugge all'espressione regolare può essere abbreviata da alcuni caratteri. Penso che sarebbe bene mantenere coerente la regex in fuga (potresti usarla altrove), ma vale la pena prendere in considerazione i loro suggerimenti.


Appunti

Entrambe queste funzioni non verranno gestite nullo undefined, ovvero se è presente un cookie denominato "null", readCookie(null)restituirà il suo valore. Se è necessario gestire questo caso, adattare il codice di conseguenza.


Qual è il vantaggio di #\salla fine del vostro sostituto-Regex? Quella sostituzione generica può essere semplificata un po 'di più (-, non ha alcun significato se non tra parentesi che sono sfuggite): /[[\]{}()*+?.\\^$|]/g può essere anche encodeURIComponent/[()*.\\]/g
smistamento

@Rudu Quel regex che fuoriesce da regex proviene da Simon Willison ed è anche usato dalla libreria XRegExp . È pensato per il caso generico (ad es. Se lo stai facendo, new RegExp('[' + str + ']')allora vorresti scappare -), quindi probabilmente hai ragione che può essere abbreviato. Dal momento che risparmierebbe solo pochi byte e la fuga di caratteri extra non influisce sulla regex finale, sono propenso a lasciarlo così com'è.
Jeffery fino

@Rudu Inoltre eviterei encodeURIComponent()in questo caso perché penso che l'OP stia cercando una funzione generica in grado di funzionare con qualsiasi nome di cookie. Se il codice di terze parti imposta un cookie denominato "foo: bar", l'utilizzo encodeURIComponent()significa tentare di leggere un cookie denominato "foo% 3Abar".
Jeffery fino

So da dove viene il codice - non è perfetto (si trasformerà a<tab>bin a\<tab>b... il che non è una fuga valida, anche se lo \<space>è). E # sembra non avere alcun significato in JS RegEx
Rudu,

1
Molte librerie di terze parti usano encodeURIComponent, se un cookie fosse nominato foo=sarebbe memorizzato come foo%3Dsenza codificare allo stesso modo il nome non lo troverai (il tuo codice non lo troverà) - non c'è una risposta giusta. Se riesci a controllare come vengono inseriti i cookie, puoi semplificare la risposta / fare ipotesi per aiutarti a diffonderlo. Il più semplice / migliore è usare solo a-zA-Z0-9 per il nome del cookie ed evitare tutto il encodeURIComponentcasino di fuga di RegExp. Ma potresti ancora doverti preoccupare decodeURIComponentdel valore dei cookie poiché questa è una pratica consigliata.
Rudu,

14

codice da 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
}

Ho cambiato l'ultima riga return d[0];e poi ho usato if (c('EXAMPLE_CK') == null)per verificare se il cookie non è definito.
electroid

9

Che ne dici di questo?

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

Contati 89 byte senza il nome della funzione.


Risposta intelligente. Al punto. Merita più voti. kpotrebbe essere chiamatokey per chiarezza, ma comunque.
Gerold Broser,

Grazie :-) Ormai la risposta accettata è stata aggiornata e contiene anche questa. Ma in una forma ancora più compressa con solo 73 caratteri.
Simon Steinberger,

Quindi, vedrai che stai mirando alla brevità. Perché non è Programmazione Puzzle & Golf codice sul tuo elenco dei siti poi ... ancora? Divertiti! E, a proposito, anche io sono uno scalatore. Berg Heil! ;)
Gerold Broser il

4

Qui va .. Saluti!

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

Nota che ho usato le stringhe di template di ES6 per comporre l'espressione regex.


3

questo in un oggetto che puoi leggere, scrivere, sovrascrivere ed eliminare i cookie.

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

1

Entrambe queste funzioni sembrano ugualmente valide in termini di lettura dei cookie. Puoi radere qualche byte (e sta davvero entrando nel territorio di Code Golf qui):

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

Tutto ciò che ho fatto è stato comprimere tutte le dichiarazioni delle variabili in un'istruzione var, rimuovere i secondi argomenti non necessari nelle chiamate alla sottostringa e sostituire l'unica chiamata CharAt in una dereference array.

Questo non è ancora breve come la seconda funzione che hai fornito, ma anche quello può avere qualche byte rimosso:

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

Ho modificato la prima sottoespressione nell'espressione regolare in una sottoespressione di acquisizione e ho modificato la parte del risultato [1] in modo che il risultato [2] coincidesse con questo cambiamento; rimosso anche le parentesi non necessarie attorno al risultato [2].


1

Per rimuovere veramente quanto più gonfio possibile, considera di non utilizzare affatto una funzione wrapper:

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

Finché hai familiarità con RegEx, quel codice è ragionevolmente pulito e di facile lettura.


0

(modifica: prima ha pubblicato la versione sbagliata .. e una non funzionale in quella. Aggiornato alla corrente, che utilizza una funzione unparam che è molto simile al secondo esempio.)

Bella idea nel primo esempio di lupi. Ho sviluppato entrambi per una funzione di lettura / scrittura dei cookie abbastanza compatta che funziona su più sottodomini. Ho pensato di condividere nel caso in cui qualcuno si imbattesse in questo thread a cercarlo.

(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)
  • Se non passi nulla a rwCookie, tutti i cookie verranno archiviati
  • Passato a rwCookie il nome di un cookie, ottiene il valore di quel cookie dalla memoria
  • Passato un valore di cookie, scrive il cookie e mette il valore in memoria
  • Scadenza predefinita per sessione a meno che non ne specifichi uno

0

Usando la risposta dei lupi, ma non usando una chiusura né un hash pre-calcolato:

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

... e minimizzando ...

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

... è uguale a 127 byte.


0

Ecco la soluzione più semplice utilizzando le funzioni stringa javascript.

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

0

La seguente funzione consentirà di distinguere tra stringhe vuote e cookie non definiti. I cookie indefiniti restituiranno correttamente undefinede non una stringa vuota a differenza di alcune delle altre risposte qui. Ma non funzionerà su IE7 e versioni precedenti, poiché non consentono l'accesso dell'array agli indici di stringa.

    function getCookie(name) {
      return (document.cookie.match('(?:^|;) *'+name+'=([^;]*)')||"")[1];
    }

Perché stai restituendo il terzo elemento nell'elenco?
nasch

Perché non ho creato il primo gruppo senza catturare. Ho aggiornato il codice.
Joyce Babu,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.