Jaka jest najkrótsza funkcja do odczytu ciasteczka według nazwy w JavaScript?

194

Jaka jest najkrótsza, dokładna i kompatybilna z różnymi przeglądarkami metoda odczytu pliku cookie w JavaScript?

Bardzo często budując samodzielne skrypty (w których nie mogę mieć żadnych zewnętrznych zależności), dodajęreadCookie() funkcję do czytania plików cookie i zwykle korzystam z metody QuirksMode.org (280 bajtów, 216 min.)

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

Robi robotę, ale jest brzydka i za każdym razem dodaje sporo wzdęci.

Metoda, w której jQuery.cookie używa czegoś takiego (zmodyfikowany, 165 bajtów, 125 minut):

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

Uwaga: nie jest to konkurs „Code Golf”: jestem prawnie zainteresowany zmniejszeniem wielkości mojej funkcji readCookie i upewnieniem się, że posiadane rozwiązanie jest prawidłowe.

Yahel
źródło
8
Przechowywane dane cookie są trochę brzydkie, więc prawdopodobnie każda metoda ich obsługi również będzie.
mVChr
4
@mVChr poważnie. W którym momencie zdecydowano, że pliki cookie powinny być dostępne z ciągu oddzielonego średnikiem? Kiedy to był dobry pomysł?
Yahel,
7
Dlaczego to pytanie jest nadal otwarte i dlaczego ma nagrodę? Czy naprawdę jesteś tak zdesperowany, aby zaoszczędzić może 5 bajtów ???
Mark Kahn
cookieArr = document.cookie.split (';'). map (ck => {return {[ck.split ('=') [0] .trim ()]: ck.split ('=') [1] }})
vladimir.gorea

Odpowiedzi:

197

Krótszy, bardziej niezawodny i skuteczniejszy niż obecnie najlepiej głosowana odpowiedź:

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

Porównanie wydajności różnych podejść pokazano tutaj:

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

Kilka uwag na temat podejścia:

Metoda regex jest nie tylko najszybsza w większości przeglądarek, ale także zapewnia najkrótszą funkcję. Ponadto należy zauważyć, że zgodnie z oficjalną specyfikacją (RFC 2109) odstęp po średniku oddzielającym pliki cookie w dokumencie. Plik cookie jest opcjonalny i można argumentować, że nie można się na niego opierać. Dodatkowo spacja jest dozwolona przed i po znaku równości (=) i można argumentować, że ta potencjalna biała spacja powinna być uwzględniona w dowolnym wiarygodnym parserze document.cookie. Wyrażenie regularne powyżej uwzględnia oba powyższe warunki spacji.

Prochowiec
źródło
4
Właśnie zauważyłem, że w Firefox, metoda wyrażeń regularnych, którą opublikowałem powyżej, nie jest tak wydajna jak metoda zapętlania. Testy, które wcześniej uruchomiłem, zostały przeprowadzone w Chrome, gdzie podejście wyrażenia regularnego działało nieco lepiej niż inne podejścia. Niemniej jednak jest to wciąż najkrótsza odpowiedź na zadawane pytanie.
Mac
6
Dlaczego getCookieValue(a, b)bierze parametr b?
Brent Washburne,
15
Głosowałem, ale nie ze względu na czytelność ... zajęło mi trochę czasu, aby dowiedzieć się, co ai brobić.
Gigi,
9
Sprytnie, ale głupio jest napisać w ten sposób, aby zaoszczędzić 1 bajt.
The Muffin Man
5
aparametr nie jest poprzedzany znakiem ucieczki, chociaż może być użyteczny, nie jest bezpieczny. getCookieValue('.*')
Takie
185

To tylko raz trafi na document.cookie JEDEN raz. Każde kolejne żądanie będzie natychmiastowe.

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

Obawiam się, że tak naprawdę nie ma szybszego sposobu niż ta ogólna logika, chyba że możesz swobodnie korzystać z .forEachfunkcji zależnych od przeglądarki (nawet wtedy nie oszczędzasz zbyt wiele)

Twój własny przykład lekko skompresowany do 120 bytes:

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

Możesz to zrobić, 110 bytesjeśli uczynisz ją 1-literową nazwą funkcji, 90 bytesjeśli upuścisz encodeURIComponent.

Sprowadziłem się do tego 73 bytes, ale szczerze mówiąc, to 82 byteskiedy jestem nazwany, readCookiea 102 byteskiedy dodaje encodeURIComponent:

function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}
Mark Kahn
źródło
Zapomniałem zwrotu, nie ma nic złego w zamknięciu, sheesh.
Mark Kahn
3
...Co? diff mówi, że to zrobiłeś. d.pr/sSte nie miał ()na końcu wywołania, więc definiował anonimową funkcję, ale nigdy jej nie wykonywał.
Yahel,
2
Proszę bardzo: jsperf.com/pre-increment-vs-post-increment Miałem rację, ++ i jest szybszy, przynajmniej jeśli dostaniesz wartość przed i po. I to właśnie robisz w pętli for.
xavierm02
1
Jedna ważna rzecz: ponieważ wartość „ciasteczek” jest buforowana, nowe ciasteczka lub zmienione ciasteczka z innych okien lub kart nie będą widoczne. Można uniknąć tego problemu, przechowując wartość ciągu z document.cookie w zmiennej i sprawdzając, czy nie zmienia się przy każdym dostępie.
Andreas
2
@StijndeWitt - jak nie odpowiada na pytanie? 73 bajty to za mało dla ciebie? :) Ostatnia odpowiedź, którą mam, jest identyczna z poniższą, z wyjątkiem niektórych kontroli białych znaków, lol
Mark Kahn
20

Założenia

Na podstawie pytania uważam, że niektóre założenia / wymagania dotyczące tej funkcji obejmują:

  • Będzie on używany jako funkcja biblioteki , a więc przeznaczony do umieszczenia w dowolnej bazie kodu;
  • Jako taki będzie musiał działać w wielu różnych środowiskach , tj. Pracować ze starszym kodem JS, CMS o różnych poziomach jakości itp .;
  • Aby współpracować z kodem napisanym przez inne osoby i / lub kodem, którego nie kontrolujesz, funkcja nie powinna przyjmować żadnych założeń dotyczących sposobu kodowania nazw lub wartości plików cookie . Wywołanie funkcji za pomocą łańcucha "foo:bar[0]"powinno zwrócić plik cookie (dosłownie) o nazwie „foo: bar [0]”;
  • Nowe pliki cookie mogą być zapisywane i / lub istniejące pliki cookie modyfikowane w dowolnym momencie życia strony.

Zgodnie z tymi założeniami jasne jest, że encodeURIComponent/ decodeURIComponent nie należy używać ; Robiąc to, zakłada się, że kod, który ustawia plik cookie, również koduje go przy użyciu tych funkcji.

Metoda wyrażeń regularnych staje się problematyczna, jeśli nazwa pliku cookie może zawierać znaki specjalne. jQuery.cookie rozwiązuje ten problem, kodując nazwę pliku cookie (w rzeczywistości zarówno nazwę, jak i wartość) podczas przechowywania pliku cookie oraz dekodując nazwę podczas pobierania pliku cookie. Rozwiązanie wyrażeń regularnych znajduje się poniżej.

O ile nie czytasz tylko plików cookie, które kontrolujesz całkowicie, wskazane byłoby również odczytywanie plików cookie document.cookiebezpośrednio i nie buforowanie wyników, ponieważ nie ma sposobu, aby dowiedzieć się, czy pamięć podręczna jest nieprawidłowa bez document.cookieponownego czytania .

(Podczas gdy uzyskiwanie dostępu i parsowanie document.cookiesbędzie nieco wolniejsze niż w przypadku użycia pamięci podręcznej, nie będzie tak wolne jak czytanie innych części DOM, ponieważ pliki cookie nie odgrywają roli w drzewach DOM / renderowania).


Funkcja oparta na pętli

Oto odpowiedź Code Golf, oparta na funkcji PPK (pętli):

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

który po zminimalizowaniu ma 128 znaków (nie licząc nazwy funkcji):

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

Funkcja oparta na wyrażeniach regularnych

Aktualizacja: jeśli naprawdę potrzebujesz rozwiązania wyrażeń regularnych:

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

Pozwala to uniknąć znaków specjalnych w nazwie pliku cookie przed zbudowaniem obiektu RegExp. Zminimalizowane, zawiera 134 znaki (nie licząc nazwy funkcji):

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

Jak zauważyli Rudu i cwolves w komentarzach, wyrażenie regularne uciekające od wyrażenia regularnego można skrócić o kilka znaków. Myślę, że dobrze byłoby zachować spójność uciekającego wyrażenia regularnego (być może używasz go gdzie indziej), ale ich sugestie są warte rozważenia.


Notatki

Obie te funkcje nie będą obsługiwać nulllub undefined, tj. Jeśli istnieje plik cookie o nazwie „null”, readCookie(null)zwróci jego wartość. Jeśli musisz poradzić sobie z tą sprawą, odpowiednio dostosuj kod.

Jeffery To
źródło
Jaka jest korzyść z #\szakończenia wymiany Regex? Tę ogólną zamianę można uprościć jeszcze bardziej (-, nie mają żadnego znaczenia, z wyjątkiem nawiasów, które są /[[\]{}()*+?.\\^$|]/gencodeURIComponent/[()*.\\]/g
poprzedzane znakami
@Rudu Ten wyrażenie uciekające wyrażenia regularne pochodzi od Simona Willisona i jest również używane przez bibliotekę XRegExp . Jest przeznaczony do ogólnego przypadku (np. Jeśli robisz new RegExp('[' + str + ']'), to chciałbyś uciec -), więc prawdopodobnie masz rację, że można go skrócić. Ponieważ pozwoliłoby to zaoszczędzić tylko kilka bajtów, a ucieczka dodatkowych znaków nie wpływa na ostateczną regex, jestem skłonny pozostawić ją taką, jaka jest.
Jeffery Do
@ Rudu Również unikałbym encodeURIComponent()w tym przypadku, ponieważ myślę, że OP szuka ogólnej funkcji, która może działać z dowolną nazwą pliku cookie. Jeśli kod innej firmy ustawi plik cookie o nazwie „foo: bar”, użycie encodeURIComponent()oznacza próbę odczytania pliku cookie o nazwie „foo% 3Abar”.
Jeffery Do
Wiem, skąd pochodzi kod - nie jest idealny (zamieni się a<tab>bw a\<tab>b... co nie jest prawidłową ucieczką, chociaż \<space>jest). I # wydaje się nie mieć znaczenia w JS RegEx
Rudu
1
Wiele bibliotek z trzeciej części używa encodeURIComponent, jeśli plik cookie foo=zostałby nazwany , byłby przechowywany, ponieważ foo%3Dbez kodowania nazwy nie można go znaleźć (kod go nie znajdzie) - nie ma właściwej odpowiedzi. Jeśli możesz kontrolować sposób umieszczania plików cookie, możesz uprościć odpowiedź / poczynić założenia, które pomogą je uzyskać. Najprostszym / najlepszym jest użycie tylko a-zA-Z0-9 dla nazwy pliku cookie i unikanie całego encodeURIComponentbałaganu ucieczki RegExp. Ale nadal możesz martwić decodeURIComponentsię wartością pliku cookie, ponieważ jest to zalecana praktyka.
Rudu
14

kod z 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
źródło
Zmieniłem ostatni wiersz, return d[0];a następnie if (c('EXAMPLE_CK') == null)sprawdziłem, czy plik cookie nie jest zdefiniowany.
electroid
9

Co powiesz na ten?

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

Zliczono 89 bajtów bez nazwy funkcji.

Simon Steinberger
źródło
Sprytna odpowiedź. Do momentu. Zasługuje na więcej pochwał. kmożna nazwaćkey dla jasności, ale i tak.
GeroldBroser przywraca Monikę
Dzięki :-) Do tej pory zaakceptowana odpowiedź została zaktualizowana i również ją zawiera. Ale w jeszcze nieco bardziej skompresowanej formie z zaledwie 73 znakami.
Simon Steinberger,
Widzisz, dążysz do skrócenia. Dlaczego Programowanie Puzzle i Code Golf nie znajduje się na twojej liście witryn ... jeszcze? Baw się dobrze! A BTW, ja też jestem wspinaczem. Berg Heil! ;)
GeroldBroser przywraca Monikę
4

Proszę bardzo ... Pozdrawiam!

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

Zauważ, że wykorzystałem ciągi szablonów ES6 do skomponowania wyrażenia regularnego.

Mfalade
źródło
3

w obiekcie, który można czytać, zapisywać, zastępować i usuwać pliki 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;
    }
};

źródło
1

Obie te funkcje wyglądają równie poprawnie pod względem odczytu plików cookie. Możesz jednak ogolić kilka bajtów (i tak naprawdę dostaje się tutaj na terytorium 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;
}

Wszystko, co z tym zrobiłem, to zwinięcie wszystkich deklaracji zmiennych w jedną instrukcję var, usunięcie niepotrzebnych drugich argumentów w wywołaniach podłańcuchu i zastąpienie jednego wywołania charAt dereferencją tablicową.

To wciąż nie jest tak krótkie jak druga funkcja, którą podałeś, ale nawet to może mieć kilka bajtów zdjętych:

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

Zmieniłem pierwsze podwyrażenie w wyrażeniu regularnym na przechwytywanie podwyrażenia i zmieniłem część wynikową [1] na wynikową [2], aby pokrywała się z tą zmianą; usunięto także niepotrzebne skrawki wokół wyniku [2].

Jeff Avallone
źródło
1

Aby naprawdę usunąć jak najwięcej wzdęć, należy w ogóle nie używać funkcji otoki:

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

O ile znasz RegEx, kod ten jest dość czysty i łatwy do odczytania.

Abhi Beckert
źródło
0

(edytuj: najpierw opublikowałem niewłaściwą wersję .. i niefunkcjonalną. Zaktualizowano do bieżącej, która używa funkcji unparam, która jest bardzo podobna do drugiego przykładu).

Fajny pomysł w pierwszym przykładzie cwolves. Zbudowałem oba w celu uzyskania dość zwartej funkcji odczytu / zapisu plików cookie, która działa w wielu subdomenach. Pomyślałem, że podzielę się, na wypadek, gdyby ktoś szukał tego wątku.

(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)
  • Jeśli przekażesz rwCookie nic, wszystkie pliki cookie zostaną zapisane w pamięci plików cookie
  • Przekazał rwCookie nazwę pliku cookie, pobiera wartość tego pliku cookie z pamięci
  • Przeszedł wartość pliku cookie, zapisuje plik cookie i umieszcza wartość w pamięci
  • Data wygaśnięcia jest domyślnie ustawiona na sesję, chyba że ją podasz
Adam
źródło
0

Używając odpowiedzi cwolves, ale nie używając zamknięcia ani wcześniej obliczonego skrótu:

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

... i minimalizowanie ...

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

... równa się 127 bajtów.

Félix Saparelli
źródło
0

Oto najprostsze rozwiązanie z wykorzystaniem funkcji ciągu javascript.

document.cookie.substring(document.cookie.indexOf("COOKIE_NAME"), 
                          document.cookie.indexOf(";", 
                          document.cookie.indexOf("COOKIE_NAME"))).
  substr(COOKIE_NAME.length);
Kumar
źródło
0

Poniższa funkcja pozwoli rozróżnić puste ciągi znaków i niezdefiniowane pliki cookie. Niezdefiniowane pliki cookie zostaną poprawnie zwrócone, undefineda nie pusty ciąg, w przeciwieństwie do niektórych innych odpowiedzi tutaj. Ale to nie będzie działać na IE7 i niższych, ponieważ nie pozwalają one na dostęp tablicy do indeksów ciągów.

    function getCookie(name) {
      return (document.cookie.match('(?:^|;) *'+name+'=([^;]*)')||"")[1];
    }
Joyce Babu
źródło
Dlaczego zwracasz trzeci element na liście?
nasch
Ponieważ nie sprawiłem, że pierwsza grupa nie schwytała. Zaktualizowałem kod.
Joyce Babu