Was ist die kürzeste Funktion zum Lesen eines Cookies nach Namen in JavaScript?

193

Was ist die kürzeste, genaueste und browserübergreifendste Methode zum Lesen eines Cookies in JavaScript?

Sehr oft fügereadCookie() ich beim Erstellen von eigenständigen Skripten (bei denen ich keine externen Abhängigkeiten haben kann) eine Funktion zum Lesen von Cookies hinzu und greife normalerweise auf die QuirksMode.org- Methode zurück (280 Byte, 216 minimiert).

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

Es macht den Job, aber es ist hässlich und fügt jedes Mal einiges an Blähungen hinzu.

Die Methode, die jQuery.cookie verwendet , ist ungefähr so (geändert, 165 Byte, 125 minimiert):

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

Beachten Sie, dass dies kein "Code Golf" -Wettbewerb ist: Ich bin zu Recht daran interessiert, die Größe meiner readCookie-Funktion zu reduzieren und sicherzustellen, dass meine Lösung gültig ist.

Yahel
quelle
8
Gespeicherte Cookie-Daten sind irgendwie hässlich, daher wird es wahrscheinlich auch eine Methode geben, mit ihnen umzugehen.
MVChr
4
@ MVChr ernst. Ab wann wurde entschieden, dass auf Cookies über eine durch Semikolon getrennte Zeichenfolge zugegriffen werden soll? Wann war das jemals eine gute Idee?
Yahel
7
Warum ist diese Frage noch offen und warum gibt es ein Kopfgeld? Sind Sie wirklich so verzweifelt, vielleicht 5 Bytes zu sparen?
Mark Kahn
cookieArr = document.cookie.split (';'). map (ck => {return {[ck.split ('=') [0] .trim ()]: ck.split ('=') [1] }})
vladimir.gorea

Antworten:

196

Kürzer, zuverlässiger und leistungsfähiger als die derzeit am besten gewählte Antwort:

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

Ein Leistungsvergleich verschiedener Ansätze wird hier gezeigt:

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

Einige Hinweise zum Ansatz:

Der Regex-Ansatz ist in den meisten Browsern nicht nur der schnellste, sondern bietet auch die kürzeste Funktion. Darüber hinaus sollte darauf hingewiesen werden, dass gemäß der offiziellen Spezifikation (RFC 2109) das Leerzeichen nach dem Semikolon, das Cookies im document.cookie trennt, optional ist und ein Argument angeführt werden könnte, auf das man sich nicht verlassen sollte. Darüber hinaus ist ein Leerzeichen vor und nach dem Gleichheitszeichen (=) zulässig, und es könnte argumentiert werden, dass dieses potenzielle Leerzeichen in einen zuverlässigen document.cookie-Parser einbezogen werden sollte. Der obige reguläre Ausdruck berücksichtigt beide oben genannten Leerzeichenbedingungen.

Mac
quelle
4
Ich habe gerade bemerkt, dass in Firefox der oben veröffentlichte Regex-Ansatz nicht so performant ist wie der Loop-Ansatz. Die Tests, die ich zuvor durchgeführt hatte, wurden in Chrome durchgeführt, wo der Regex-Ansatz geringfügig besser abschnitt als andere Ansätze. Trotzdem ist es immer noch das kürzeste, das sich mit der gestellten Frage befasst.
Mac
6
Warum nimmt getCookieValue(a, b)Parameter b?
Brent Washburne
15
Upvoted, aber nicht aus Gründen der Lesbarkeit ... Ich habe eine Weile gebraucht, um herauszufinden, was aund was bich tun soll.
Gigi
9
Clever, aber albern, es so zu schreiben, um 1 Byte zu sparen.
Der Muffin-Mann
5
aDer Parameter ist nicht mit einem regulären Ausdruck versehen, obwohl er nützlich sein kann, aber nicht sicher ist. Dinge wie getCookieValue('.*')werden jeden zufälligen Cookie zurückgeben
Vitim.us
185

Dies wird immer nur EINMAL auf document.cookie treffen. Jede nachfolgende Anfrage erfolgt sofort.

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

Ich fürchte, es gibt wirklich keinen schnelleren Weg als diese allgemeine Logik, es sei denn, Sie können sie verwenden, .forEachdie vom Browser abhängig ist (selbst dann sparen Sie nicht so viel).

Ihr eigenes Beispiel leicht komprimiert auf 120 bytes:

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

Sie können es erreichen, 110 byteswenn Sie es zu einem 1-Buchstaben-Funktionsnamen machen, 90 byteswenn Sie das löschen encodeURIComponent.

Ich habe es auf den Punkt gebracht 73 bytes, aber um fair zu sein, 82 byteswenn es benannt wird readCookieund 102 bytesdann hinzugefügt wird encodeURIComponent:

function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}
Mark Kahn
quelle
Ich habe die Rückgabeerklärung vergessen, es ist nichts Falsches an der Schließung, meine Güte.
Mark Kahn
3
...Was? Diff sagt, du hast es getan. d.pr/sSte hatte den ()Aufruf am Ende nicht, definierte also eine anonyme Funktion, führte sie jedoch nie aus.
Yahel
2
Los geht's: jsperf.com/pre-increment-vs-post-increment Ich hatte Recht, ++ Ich bin schneller, zumindest wenn Sie den Wert vorher und nachher erhalten. Und genau das tun Sie in einer for-Schleife.
Xavierm02
1
Eine wichtige Sache: Da der Wert von "Cookies" zwischengespeichert wird, werden neue Cookies oder geänderte Cookies aus anderen Fenstern oder Registerkarten nicht sichtbar. Sie können dieses Problem vermeiden, indem Sie den Zeichenfolgenwert aus document.cookie in einer Variablen speichern und prüfen, ob er bei jedem Zugriff unverändert bleibt.
Andreas
2
@StijndeWitt - wie funktioniert es nicht die Frage beantworten? 73 Bytes sind nicht kurz genug für Sie? :) Die letzte Antwort, die ich habe, ist identisch mit der unten stehenden, bis auf einige Leerzeichenprüfungen, lol
Mark Kahn
20

Annahmen

Aufgrund der Frage glaube ich, dass einige Annahmen / Anforderungen für diese Funktion Folgendes umfassen:

  • Es wird als Bibliotheksfunktion verwendet und soll daher in eine beliebige Codebasis verschoben werden.
  • Als solches muss es in vielen verschiedenen Umgebungen funktionieren, dh mit Legacy-JS-Code, CMS verschiedener Qualitätsstufen usw.;
  • Um mit Code zu interagieren, der von anderen Personen geschrieben wurde, und / oder mit Code, den Sie nicht kontrollieren, sollte die Funktion keine Annahmen darüber treffen, wie Cookie-Namen oder -Werte codiert werden . Das Aufrufen der Funktion mit einer Zeichenfolge "foo:bar[0]"sollte ein Cookie (wörtlich) mit dem Namen "foo: bar [0]" zurückgeben.
  • Neue Cookies können zu jedem Zeitpunkt während der Lebensdauer der Seite geschrieben und / oder vorhandene Cookies geändert werden .

Unter diesen Annahmen ist klar, dass encodeURIComponent/ decodeURIComponent nicht verwendet werden sollte ; Dabei wird davon ausgegangen, dass der Code, der das Cookie gesetzt hat, es auch mit diesen Funktionen codiert hat.

Der Ansatz mit regulären Ausdrücken wird problematisch, wenn der Cookie-Name Sonderzeichen enthalten kann. jQuery.cookie umgeht dieses Problem, indem es beim Speichern eines Cookies den Cookie-Namen (eigentlich Name und Wert) codiert und beim Abrufen eines Cookies den Namen decodiert.Eine Lösung für reguläre Ausdrücke finden Sie weiter unten.

Wenn Sie nicht nur Cookies lesen, die Sie vollständig kontrollieren, ist es auch ratsam, Cookies document.cookiedirekt zu lesen und die Ergebnisse nicht zwischenzuspeichern, da es keine Möglichkeit gibt, festzustellen, ob der Cache ohne Lesen ungültig istdocument.cookie erneut .

(Während der Zugriff und das Parsen document.cookiesetwas langsamer sind als die Verwendung eines Caches, ist er nicht so langsam wie das Lesen anderer Teile des DOM, da Cookies in den DOM- / Render-Bäumen keine Rolle spielen.)


Schleifenbasierte Funktion

Hier ist die Antwort von Code Golf, basierend auf der (schleifenbasierten) Funktion von 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, '');
}

was bei Minimierung 128 Zeichen ergibt (ohne den Funktionsnamen):

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

Funktion auf der Basis regulärer Ausdrücke

Update: Wenn Sie wirklich eine Lösung für reguläre Ausdrücke wünschen:

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

Dieses entgeht keine Sonderzeichen in den Cookie - Namen , bevor Sie das RegExp - Objekt zu konstruieren. Minimiert sind dies 134 Zeichen (ohne den Funktionsnamen):

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

Wie Rudu und cwolves in den Kommentaren hervorgehoben haben, kann der reguläre Ausdruck, der dem regulären Ausdruck entgeht, um einige Zeichen verkürzt werden. Ich denke, es wäre gut, den entweichenden regulären Ausdruck konsistent zu halten (Sie können ihn an anderer Stelle verwenden), aber ihre Vorschläge sind eine Überlegung wert.


Anmerkungen

Diese beiden Funktionen werden nicht verarbeitet nulloder geben den Wert zurück undefined, wenn ein Cookie mit dem Namen "null" vorhanden ist readCookie(null). Wenn Sie diesen Fall behandeln müssen, passen Sie den Code entsprechend an.

Jeffery To
quelle
Was ist der Vorteil von #\sam Ende Ihres Ersatz-Regex? Das generische Ersetzen kann etwas weiter vereinfacht werden (-, hat keine Bedeutung außer in Klammern, die /[[\]{}()*+?.\\^$|]/gencodeURIComponent/[()*.\\]/g
maskiert
@Rudu Dieser Regex-entkommende Regex stammt von Simon Willison und wird auch von der XRegExp- Bibliothek verwendet. Es ist für den allgemeinen Fall gedacht (z. B. wenn Sie dies tun new RegExp('[' + str + ']'), möchten Sie entkommen -), sodass Sie wahrscheinlich Recht haben, dass es verkürzt werden kann. Da nur wenige Bytes eingespart werden und das Entkommen zusätzlicher Zeichen keinen Einfluss auf die endgültige Regex hat, neige ich dazu, sie so zu belassen, wie sie ist.
Jeffery bis
@ Rudu Auch ich würde encodeURIComponent()in diesem Fall vermeiden , weil ich denke, dass das OP nach einer generischen Funktion sucht, die mit jedem Cookie-Namen funktionieren kann. Wenn der Code eines Drittanbieters ein Cookie mit dem Namen "foo: bar" setzt, bedeutet die Verwendung encodeURIComponent(), dass versucht wird, ein Cookie mit dem Namen "foo% 3Abar" zu lesen.
Jeffery bis
Ich weiß, woher der Code kommt - er ist nicht perfekt (wird sich a<tab>bin a\<tab>b... verwandeln, was jedoch keine gültige Flucht \<space>ist). Und # scheint in JS RegEx
Rudu
1
Viele Bibliotheken des dritten Teils verwenden encodeURIComponent. Wenn ein Cookie benannt wurde, wird foo=es so gespeichert, als ob Sie foo%3Dden Namen nicht finden würden, ohne ihn ebenfalls zu codieren (Ihr Code findet ihn nicht) - es gibt keine richtige Antwort. Wenn Sie steuern können, wie die Cookies eingefügt werden, können Sie die Antwort vereinfachen / Annahmen treffen, um das Herausholen zu erleichtern. Am einfachsten / besten ist es, nur a-zA-Z0-9 für den Cookie-Namen zu verwenden und zu vermeiden, dass das Ganze encodeURIComponentund RegExp dem Chaos entkommen. Sie können aber nach wie vor zu befürchten müssen decodeURIComponentauf Cookie - Wert , da dies eine empfohlene Praxis ist.
Rudu
14

Code von 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
quelle
Ich habe die letzte Zeile geändert return d[0];und dann if (c('EXAMPLE_CK') == null)überprüft, ob kein Cookie definiert ist.
Electroid
9

Wie wäre es mit diesem?

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

89 Bytes ohne Funktionsnamen gezählt.

Simon Steinberger
quelle
Kluge Antwort. Auf den Punkt. Verdient mehr positive Stimmen. kkönntekey für Klarheit benannt werden, aber trotzdem.
GeroldBroser setzt Monica
Danke :-) Inzwischen wurde die akzeptierte Antwort aktualisiert und enthält auch diese. Aber in einer noch etwas komprimierteren Form mit nur 73 Zeichen.
Simon Steinberger
Ich verstehe, Sie streben nach Kürze. Warum steht Programming Puzzles & Code Golf dann noch nicht auf Ihrer Site-Liste ? Habe Spaß! Übrigens bin ich auch Kletterer. Berg Heil! ;)
GeroldBroser stellt Monica am
4

Hier geht .. Prost!

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

Beachten Sie, dass ich die Vorlagenzeichenfolgen von ES6 verwendet habe, um den Regex-Ausdruck zu erstellen.

mfalade
quelle
3

Dies in einem Objekt, das Sie lesen, schreiben, überschreiben und Cookies löschen können.

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

quelle
1

Beide Funktionen sehen beim Lesen von Cookies gleichermaßen gültig aus. Sie können sich jedoch ein paar Bytes rasieren (und es betritt hier wirklich das Gebiet von Code Golf):

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

Alles, was ich damit gemacht habe, ist, alle Variablendeklarationen in einer var-Anweisung zusammenzufassen, die unnötigen zweiten Argumente bei Aufrufen von Teilzeichenfolgen zu entfernen und den einen charAt-Aufruf in eine Array-Dereferenzierung zu ersetzen.

Dies ist immer noch nicht so kurz wie die zweite Funktion, die Sie bereitgestellt haben, aber selbst das kann ein paar Bytes entfernen:

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

Ich habe den ersten Unterausdruck im regulären Ausdruck in einen erfassenden Unterausdruck geändert und den Teil Ergebnis [1] in Ergebnis [2] geändert, um mit dieser Änderung übereinzustimmen. entfernte auch die unnötigen Parens um das Ergebnis [2].

Jeff Avallone
quelle
1

Um wirklich so viel Aufblähung wie möglich zu entfernen, sollten Sie keine Wrapper-Funktion verwenden:

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

Solange Sie mit RegEx vertraut sind, ist dieser Code einigermaßen sauber und leicht zu lesen.

Abhi Beckert
quelle
0

(Bearbeiten: zuerst die falsche Version gepostet .. und eine nicht funktionierende. Aktualisiert auf den aktuellen Stand, der eine unparam-Funktion verwendet, die dem zweiten Beispiel sehr ähnlich ist.)

Gute Idee im ersten Beispiel cwolves. Ich habe auf beiden aufgebaut, um eine ziemlich kompakte Lese- / Schreibfunktion für Cookies zu erhalten, die über mehrere Subdomänen hinweg funktioniert. Ich dachte, ich würde es teilen, falls jemand anderes auf diesen Thread stößt und danach sucht.

(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)
  • Wenn Sie rwCookie nichts übergeben, werden alle Cookies in den Cookie-Speicher verschoben
  • Wenn rwCookie einen Cookie-Namen übergeben wird, wird der Wert dieses Cookies aus dem Speicher abgerufen
  • Hat einen Cookie-Wert übergeben, schreibt er den Cookie und speichert den Wert
  • Der Ablauf ist standardmäßig auf Sitzung eingestellt, sofern Sie keine angeben
Adam
quelle
0

Verwenden Sie die Antwort von cwolves, aber weder einen Abschluss noch einen vorberechneten Hash:

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

... und minimieren ...

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

... entspricht 127 Bytes.

Félix Saparelli
quelle
0

Hier ist die einfachste Lösung mit Javascript-Zeichenfolgenfunktionen.

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

Die folgende Funktion ermöglicht die Unterscheidung zwischen leeren Zeichenfolgen und undefinierten Cookies. Undefinierte Cookies werden korrekt zurückgegeben undefinedund im Gegensatz zu einigen anderen Antworten hier keine leere Zeichenfolge. Unter IE7 und darunter funktioniert dies jedoch nicht, da sie keinen Array-Zugriff auf Zeichenfolgenindizes ermöglichen.

    function getCookie(name) {
      return (document.cookie.match('(?:^|;) *'+name+'=([^;]*)')||"")[1];
    }
Joyce Babu
quelle
Warum geben Sie den dritten Eintrag in der Liste zurück?
Nasch
Weil ich die erste Gruppe nicht nicht erfasst habe. Ich habe den Code aktualisiert.
Joyce Babu