Apa fungsi terpendek untuk membaca cookie dengan nama di JavaScript?

193

Apa metode yang paling singkat, akurat, dan kompatibel dengan peramban silang untuk membaca cookie dalam JavaScript?

Sangat sering, ketika membangun skrip yang berdiri sendiri (di mana saya tidak dapat memiliki dependensi luar), saya menemukan diri saya menambahkan fungsi untuk membaca cookie, dan biasanya mundur pada metode QuirksMode.orgreadCookie() (280 byte, 216 diperkecil).

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

Itu melakukan pekerjaan, tapi itu jelek, dan menambahkan sedikit mengasapi setiap kali.

Metode yang menggunakan jQuery.cookie seperti ini (dimodifikasi, 165 byte, 125 diperkecil):

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

Perhatikan ini bukan kompetisi 'Code Golf': Saya secara sah tertarik mengurangi ukuran fungsi readCookie saya, dan memastikan solusi yang saya miliki valid.

Yahel
sumber
8
Data cookie yang disimpan agak jelek, jadi metode apa pun untuk menanganinya mungkin juga akan baik.
mVChr
4
@ mvchr serius. Pada titik mana diputuskan bahwa cookie harus diakses dari string yang dibatasi semi-colon? Kapan itu ide yang bagus?
Yahel
7
Mengapa pertanyaan ini masih terbuka dan mengapa ia memiliki karunia? Apakah Anda benar-benar putus asa untuk menyimpan mungkin 5 byte ???
Mark Kahn
cookieArr = document.cookie.split (';'). map (ck => {return {[ck.split ('=') [0] .trim ()]: ck.split ('=') [1] }})
vladimir.gorea

Jawaban:

196

Lebih pendek, lebih dapat diandalkan, dan lebih berkinerja daripada jawaban terbaik saat ini:

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

Perbandingan kinerja berbagai pendekatan ditampilkan di sini:

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

Beberapa catatan tentang pendekatan:

Pendekatan regex tidak hanya yang tercepat di sebagian besar browser, ia menghasilkan fungsi terpendek juga. Selain itu harus ditunjukkan bahwa sesuai dengan spesifikasi resmi (RFC 2109) , ruang setelah titik koma yang memisahkan cookie dalam dokumen.cookie adalah opsional dan argumen dapat dibuat bahwa itu tidak boleh diandalkan. Selain itu, spasi putih diizinkan sebelum dan sesudah tanda sama dengan (=) dan argumen dapat dibuat bahwa spasi putih potensial ini harus diperhitungkan dalam parser document.cookie yang dapat diandalkan. Regex di atas menyumbang kedua kondisi spasi putih di atas.

Mac
sumber
4
Saya baru saja memperhatikan bahwa di Firefox, pendekatan regex yang saya posting di atas tidak sebagus pendekatan looping. Tes yang saya jalankan sebelumnya dilakukan di Chrome, di mana pendekatan regex berkinerja lebih baik daripada pendekatan lain. Meskipun demikian, ini masih yang terpendek yang menjawab pertanyaan yang diajukan.
Mac
6
Mengapa getCookieValue(a, b)mengambil parameter b?
Brent Washburne
15
Terpilih, tetapi tidak untuk keterbacaan ... butuh beberapa saat untuk mencari tahu apa adan apa yang bharus dilakukan.
Gigi
9
Cerdik, tapi konyol untuk menulis seperti itu menghemat 1 byte.
The Muffin Man
5
aparameter tidak regex lolos, sementara itu bisa bermanfaat, itu tidak aman. Hal-hal seperti getCookieValue('.*')akan mengembalikan cookie acak
Vitim.us
185

Ini hanya akan mencapai document.cookie SATU kali. Setiap permintaan selanjutnya akan instan.

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

Saya khawatir tidak ada cara yang lebih cepat dari logika umum ini kecuali Anda bebas untuk menggunakannya .forEach yang tergantung pada browser (bahkan saat itu Anda tidak menyimpan terlalu banyak)

Contoh Anda sendiri sedikit dikompresi ke 120 bytes:

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

Anda bisa mendapatkannya 110 bytesjika Anda menjadikannya nama fungsi 1 huruf, 90 bytesjika Anda menjatuhkannyaencodeURIComponent .

Saya sudah membahasnya 73 bytes, tetapi untuk bersikap adil itu 82 bytesketika dinamai readCookiedan 102 byteskemudian menambahkan encodeURIComponent:

function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}
Mark Kahn
sumber
Saya lupa pernyataan kembali, tidak ada yang salah dengan penutupan, sheesh.
Mark Kahn
3
...Apa? beda kata kamu lakukan. d.pr/sSte itu tidak memiliki ()panggilan di akhir, jadi itu mendefinisikan fungsi anonim tetapi tidak pernah menjalankannya.
Yahel
2
Ini dia: jsperf.com/pre-increment-vs-post-increment Saya benar, ++ saya lebih cepat, setidaknya jika Anda mendapatkan nilainya sebelum dan sesudah. Dan itulah yang Anda lakukan dalam for for loop.
xavierm02
1
Satu hal penting: Karena nilai "cookie" di-cache, cookie baru atau cookie yang diubah dari jendela atau tab lain tidak akan terlihat. Anda dapat menghindari masalah ini dengan menyimpan nilai string dari document.cookie dalam variabel dan memeriksa apakah itu tidak berubah pada setiap akses.
Andreas
2
@StijndeWitt - bagaimana itu tidak menjawab pertanyaan? 73 byte tidak cukup pendek untuk Anda? :) Jawaban terakhir yang saya miliki identik dengan yang di bawah ini, kecuali untuk beberapa pemeriksaan spasi putih, lol
Mark Kahn
20

Asumsi

Berdasarkan pertanyaan, saya percaya beberapa asumsi / persyaratan untuk fungsi ini meliputi:

  • Ini akan digunakan sebagai fungsi perpustakaan , dan dimaksudkan untuk dimasukkan ke basis kode apa pun;
  • Karena itu, perlu bekerja di banyak lingkungan yang berbeda , yaitu bekerja dengan kode JS lama, CMS dari berbagai tingkat kualitas, dll .;
  • Untuk beroperasi dengan kode yang ditulis oleh orang lain dan / atau kode yang tidak Anda kontrol, fungsi tersebut tidak boleh membuat asumsi tentang bagaimana nama atau nilai cookie dikodekan . Memanggil fungsi dengan string "foo:bar[0]"harus mengembalikan cookie (secara harfiah) bernama "foo: bar [0]";
  • Cookie baru dapat ditulis dan / atau cookie yang ada dimodifikasi pada titik mana pun selama masa halaman.

Di bawah asumsi ini, jelas bahwa encodeURIComponent/ decodeURIComponent tidak boleh digunakan ; melakukan hal itu dengan asumsi bahwa kode yang mengatur cookie juga menyandikannya menggunakan fungsi-fungsi ini.

Pendekatan ekspresi reguler menjadi bermasalah jika nama cookie dapat berisi karakter khusus. jQuery.cookie mengatasi masalah ini dengan menyandikan nama cookie (sebenarnya nama dan nilai) ketika menyimpan cookie, dan mendekode nama saat mengambil cookie. Solusi ekspresi reguler di bawah.

Kecuali Anda hanya membaca cookie yang Anda kontrol sepenuhnya, juga disarankan untuk membaca cookie dari document.cookielangsung dan tidak men-cache hasil, karena tidak ada cara untuk mengetahui apakah cache tidak valid tanpa membacadocument.cookie lagi.

(Walaupun mengakses dan menguraikan document.cookiesakan sedikit lebih lambat daripada menggunakan cache, itu tidak akan selambat membaca bagian lain dari DOM, karena cookie tidak memainkan peran dalam pohon DOM / render.)


Fungsi berbasis loop

Inilah jawaban Code Golf, berdasarkan fungsi PPK (berbasis loop):

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

yang ketika diperkecil, mencapai 128 karakter (tidak termasuk nama fungsi):

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

Fungsi berbasis ekspresi reguler

Pembaruan: Jika Anda benar-benar menginginkan solusi ekspresi reguler:

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

Ini lolos dari setiap karakter khusus dalam nama cookie sebelum membangun objek RegExp. Diminimalkan, ini sampai 134 karakter (tidak termasuk nama fungsi):

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

Seperti yang ditunjukkan oleh Rudu dan cwolves dalam komentar, regex reguler-ekspresi-pelarian dapat dipersingkat oleh beberapa karakter. Saya pikir itu akan baik untuk menjaga regex melarikan diri konsisten (Anda mungkin menggunakannya di tempat lain), tetapi saran mereka patut dipertimbangkan.


Catatan

Kedua fungsi ini tidak akan menangani nullatau undefined, yaitu jika ada cookie bernama "null", readCookie(null)akan mengembalikan nilainya. Jika Anda perlu menangani kasus ini, sesuaikan kode tersebut.

Jeffery To
sumber
Apa manfaatnya #\sdi akhir penggantian-Regex Anda? Pengganti generik itu dapat disederhanakan sedikit lebih jauh (-, tidak memiliki arti kecuali dalam tanda kurung yang lolos): /[[\]{}()*+?.\\^$|]/g Itu dapat lebih encodeURIComponent/[()*.\\]/g
disortir
@Rudu Regex-escaping regex berasal dari Simon Willison dan juga digunakan oleh perpustakaan XRegExp . Ini dimaksudkan untuk kasus generik (misalnya jika Anda lakukan new RegExp('[' + str + ']')maka Anda ingin melarikan diri -), jadi Anda mungkin benar bahwa itu dapat dipersingkat. Karena itu hanya akan menyimpan beberapa byte, dan keluar dari karakter tambahan tidak mempengaruhi regex akhir, saya cenderung membiarkannya seperti itu.
Jeffery To
@Rudu Saya juga akan menghindari encodeURIComponent()dalam hal ini karena saya pikir OP sedang mencari fungsi generik yang dapat bekerja dengan nama cookie apa pun. Jika kode pihak ketiga menetapkan cookie bernama "foo: bar", menggunakan encodeURIComponent()berarti mencoba membaca cookie yang bernama "foo% 3Abar".
Jeffery To
Saya tahu dari mana kode itu berasal - itu tidak sempurna (akan berubah a<tab>bmenjadi a\<tab>b... yang bukan merupakan pelarian yang valid, meskipun demikian \<space>). Dan # tampaknya tidak ada artinya di JS RegEx
Rudu
1
Banyak pustaka bagian ketiga yang digunakan encodeURIComponent, jika cookie bernama foo=itu akan disimpan karena foo%3Dtanpa meng-encode nama Anda juga tidak akan menemukannya (kode Anda tidak akan menemukannya) - tidak ada jawaban yang tepat. Jika Anda dapat mengontrol bagaimana cookie dimasukkan maka Anda dapat menyederhanakan jawaban / membuat asumsi untuk membantu mengeluarkannya. Yang paling sederhana / terbaik adalah dengan hanya menggunakan a-zA-Z0-9 untuk nama cookie dan menghindari keseluruhan encodeURIComponentdan RegExp lolos dari kekacauan. Tapi Anda mungkin masih perlu khawatir tentang decodeURIComponentdi nilai cookie karena itulah praktek direkomendasikan.
Rudu
14

kode dari 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
sumber
Saya mengubah baris terakhir return d[0];dan kemudian saya gunakan if (c('EXAMPLE_CK') == null)untuk memeriksa apakah cookie tidak didefinisikan.
electroid
9

Bagaimana dengan yang ini?

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

Dihitung 89 byte tanpa nama fungsi.

Simon Steinberger
sumber
Jawaban cerdas. Ke titik. Pantas lebih banyak upvotes. kbisa dinamaikey jelas, tapi toh.
GeroldBroser mengembalikan Monica
Terima kasih :-) Sekarang, jawaban yang diterima telah diperbarui dan berisi yang ini juga. Tetapi dalam bentuk yang sedikit lebih terkompresi dengan hanya 73 karakter.
Simon Steinberger
Jadi, Anda mengincar kekurangan, saya mengerti. Mengapa Pemrograman Puzzle & Kode Golf tidak ada dalam daftar situs Anda ... belum? Selamat bersenang-senang! Dan, BTW, saya juga seorang pendaki. Berg Heil! ;)
GeroldBroser mengembalikan Monica
4

Ini dia .. Ceria!

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

Perhatikan bahwa saya menggunakan string template ES6 untuk menulis ekspresi regex.

mfalade
sumber
3

ini di objek yang bisa Anda baca, tulis, tulis, dan hapus 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;
    }
};

sumber
1

Kedua fungsi ini terlihat sama validnya dalam hal membaca cookie. Anda dapat mencukur beberapa byte (dan ini benar-benar masuk ke wilayah Code Golf di sini):

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

Yang saya lakukan dengan ini adalah menciutkan semua deklarasi variabel menjadi satu pernyataan var, menghapus argumen kedua yang tidak perlu dalam panggilan ke substring, dan mengganti satu panggilan charAt menjadi dereference array.

Ini masih tidak sesingkat fungsi kedua yang Anda berikan, tetapi bahkan itu dapat menghilangkan beberapa byte:

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

Saya mengubah sub-ekspresi pertama dalam ekspresi reguler menjadi menangkap sub-ekspresi, dan mengubah hasil [1] bagian untuk menghasilkan [2] untuk bertepatan dengan perubahan ini; juga menghapus parens yang tidak perlu di sekitar hasil [2].

Jeff Avallone
sumber
1

Untuk benar-benar menghilangkan bloat sebanyak mungkin, pertimbangkan untuk tidak menggunakan fungsi wrapper sama sekali:

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

Selama Anda terbiasa dengan RegEx, kode itu cukup bersih dan mudah dibaca.

Abhi Beckert
sumber
0

(sunting: memposting versi yang salah terlebih dahulu .. dan yang tidak berfungsi pada saat itu. Diperbarui ke saat ini, yang menggunakan fungsi unparam yang mirip dengan contoh kedua.)

Ide bagus di cwolves contoh pertama. Saya membangun keduanya untuk fungsi membaca / menulis cookie yang cukup ringkas yang berfungsi di beberapa subdomain. Kupikir aku akan berbagi kalau-kalau ada orang yang berlari melintasi utas ini mencari itu.

(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)
  • Jika Anda tidak memberikan apa pun kepada rwCookie, itu akan memasukkan semua cookie ke dalam penyimpanan cookie
  • Melewati rwCookie nama cookie, ia mendapat nilai cookie dari penyimpanan
  • Melewati nilai cookie, itu menulis cookie dan menempatkan nilai dalam penyimpanan
  • Kedaluwarsa default untuk sesi kecuali Anda menentukan satu
Adam
sumber
0

Menggunakan jawaban cwolves, tetapi tidak menggunakan penutupan atau hash yang sudah dihitung sebelumnya:

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

... dan mengecilkan ...

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

... sama dengan 127 byte.

Félix Saparelli
sumber
0

Berikut adalah solusi paling sederhana menggunakan fungsi string javascript.

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

Fungsi berikut akan memungkinkan membedakan antara string kosong dan cookie yang tidak terdefinisi. Cookie yang tidak terdefinisi akan kembali dengan benar undefineddan bukan string kosong seperti beberapa jawaban lain di sini. Tapi itu tidak akan berfungsi pada IE7 dan di bawah, karena mereka tidak mengizinkan akses array ke indeks string.

    function getCookie(name) {
      return (document.cookie.match('(?:^|;) *'+name+'=([^;]*)')||"")[1];
    }
Joyce Babu
sumber
Mengapa Anda mengembalikan item ketiga dalam daftar?
nasch
Karena saya tidak membuat grup pertama tidak menangkap. Saya telah memperbarui kodenya.
Joyce Babu