Hàm ngắn nhất để đọc cookie theo tên trong JavaScript là gì?


193

Phương pháp tương thích ngắn nhất, chính xác và tương thích với nhiều trình duyệt để đọc cookie trong JavaScript là gì?

Rất thường xuyên, trong khi xây dựng các tập lệnh độc lập (nơi tôi không thể có bất kỳ phụ thuộc bên ngoài nào), tôi thấy mình đã thêm một chức năng để đọc cookie và thường dựa vào phương thức QuirksMode.orgreadCookie() (280 byte, tối thiểu hóa 216).

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

Nó thực hiện công việc, nhưng nó xấu xí, và thêm khá nhiều sự phình to mỗi lần.

Phương thức mà jQuery.cookie sử dụng một cái gì đó như thế này (đã sửa đổi, 165 byte, rút ​​gọn 125):

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

Lưu ý rằng đây không phải là cuộc thi 'Code Golf': Tôi thực sự quan tâm đến việc giảm quy mô của chức năng readCookie của mình và đảm bảo giải pháp tôi có là hợp lệ.


8
Dữ liệu cookie được lưu trữ là loại xấu xí, vì vậy bất kỳ phương pháp nào để xử lý chúng có thể cũng sẽ như vậy.
mVChr

4
@mVChr nghiêm túc. Tại thời điểm nào đã quyết định rằng cookie nên được truy cập từ một chuỗi phân cách bán dấu hai chấm? Khi đó là một ý tưởng tốt?
Yahel

7
Tại sao câu hỏi này vẫn mở và tại sao nó có tiền thưởng? Bạn có thực sự tuyệt vọng để tiết kiệm có thể 5 byte ???
Mark Kahn

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

Câu trả lời:


196

Ngắn hơn, đáng tin cậy hơn và hiệu quả hơn so với câu trả lời được bình chọn tốt nhất hiện nay:

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

Một so sánh hiệu suất của các phương pháp khác nhau được hiển thị ở đây:

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

Một số lưu ý về cách tiếp cận:

Cách tiếp cận regex không chỉ nhanh nhất trong hầu hết các trình duyệt, nó còn mang lại chức năng ngắn nhất. Ngoài ra, cần chỉ ra rằng theo thông số chính thức (RFC 2109) , khoảng trắng sau dấu chấm phẩy phân tách cookie trong document.cookie là tùy chọn và có thể đưa ra một đối số mà không nên dựa vào. Ngoài ra, khoảng trắng được cho phép trước và sau dấu bằng (=) và có thể đưa ra một đối số rằng khoảng trắng tiềm năng này sẽ được đưa vào bất kỳ trình phân tích cú pháp document.cookie đáng tin cậy nào. Regex ở trên chiếm cả hai điều kiện khoảng trắng ở trên.


4
Tôi vừa nhận thấy rằng trong Firefox, cách tiếp cận regex mà tôi đã đăng ở trên không hiệu quả như cách tiếp cận lặp. Các thử nghiệm tôi đã chạy trước đây được thực hiện trong Chrome, trong đó phương pháp regex thực hiện tốt hơn một cách khiêm tốn so với các phương pháp khác. Tuy nhiên, đây vẫn là câu hỏi ngắn nhất giải quyết câu hỏi đang được hỏi.
Mac

6
Tại sao getCookieValue(a, b)có tham số b?
Brent Washburne

15
Được nâng cấp, nhưng không phải để dễ đọc ... tôi đã mất một thời gian để tìm ra những gì ablàm.
Gigi

9
Thông minh, nhưng ngớ ngẩn khi viết nó theo cách đó để tiết kiệm 1 byte.
Người đàn ông Muffin

5
atham số không phải là regex thoát, trong khi nó có thể hữu ích, nó không an toàn. Những thứ như getCookieValue('.*')sẽ trả lại bất kỳ cookie ngẫu nhiên nào
Vitim.us

185

Điều này sẽ chỉ bao giờ đạt đến document.cookie MỘT lần. Mọi yêu cầu tiếp theo sẽ ngay lập tức.

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

Tôi e rằng thực sự không có cách nào nhanh hơn logic chung này trừ khi bạn tự do sử dụng .forEachphụ thuộc vào trình duyệt (ngay cả khi bạn không tiết kiệm nhiều như vậy)

Ví dụ của riêng bạn hơi bị nén để 120 bytes:

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

Bạn có thể lấy nó 110 bytesnếu bạn đặt tên hàm 1 ký tự, 90 bytesnếu bạn bỏ encodeURIComponent.

Tôi đã nhận được nó 73 bytes, nhưng để công bằng, nó 82 bytesđược đặt tên readCookie102 byteskhi đó thêm encodeURIComponent:

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

Tôi quên mất tuyên bố trở lại, không có gì sai với việc đóng cửa, sheesh.
Mark Kahn

3
...Gì? diff nói bạn đã làm. d.pr/sSte cuối cùng nó không có ()cuộc gọi, vì vậy nó đã xác định một hàm ẩn danh nhưng không bao giờ thực hiện nó.
Yahel

2
Ở đây bạn đi: jsperf.com/pre-increment-vs-post-increment Tôi đã đúng, ++ tôi nhanh hơn, ít nhất là nếu bạn nhận được giá trị trước và sau. Và đó là những gì bạn làm trong một vòng lặp for.
xavierm02

1
Một điều quan trọng: Vì giá trị của "cookie" được lưu trong bộ nhớ cache, cookie mới hoặc cookie đã thay đổi từ các cửa sổ hoặc tab khác sẽ không hiển thị. Bạn có thể tránh vấn đề này bằng cách lưu trữ giá trị chuỗi từ document.cookie trong một biến và kiểm tra xem nó có thay đổi trên mỗi lần truy cập không.
Andreas

2
@StijndeWitt - làm thế nào mà nó không trả lời được câu hỏi? 73 byte không đủ ngắn cho bạn? :) Câu trả lời cuối cùng tôi có giống hệt câu trả lời dưới đây, ngoại trừ một số kiểm tra khoảng trắng, lol
Mark Kahn

20

Giả định

Dựa trên câu hỏi, tôi tin rằng một số giả định / yêu cầu cho chức năng này bao gồm:

  • Nó sẽ được sử dụng như một chức năng của thư viện và do đó có nghĩa là được thả vào bất kỳ cơ sở mã nào;
  • Như vậy, nó sẽ cần phải hoạt động trong nhiều môi trường khác nhau , tức là làm việc với mã JS cũ, các CMS có mức chất lượng khác nhau, v.v.;
  • Để tương tác với mã được viết bởi người khác và / hoặc mã mà bạn không kiểm soát, hàm không được đưa ra bất kỳ giả định nào về cách mã hóa tên hoặc giá trị cookie . Gọi hàm bằng một chuỗi "foo:bar[0]"sẽ trả về một cookie (theo nghĩa đen) có tên là "foo: bar [0]";
  • Cookie mới có thể được viết và / hoặc cookie hiện tại được sửa đổi tại bất kỳ thời điểm nào trong suốt vòng đời của trang.

Theo các giả định này, rõ ràng rằng encodeURIComponent/ decodeURIComponent không nên được sử dụng ; làm như vậy giả định rằng mã đặt cookie cũng mã hóa nó bằng các hàm này.

Cách tiếp cận biểu thức chính quy gặp vấn đề nếu tên cookie có thể chứa các ký tự đặc biệt. jQuery.cookie giải quyết vấn đề này bằng cách mã hóa tên cookie (thực tế là cả tên và giá trị) khi lưu trữ cookie và giải mã tên khi lấy cookie. Một giải pháp biểu hiện thông thường là dưới đây.

Trừ khi bạn chỉ đọc cookie mà bạn kiểm soát hoàn toàn, bạn cũng nên đọc cookie document.cookietrực tiếp và không lưu bộ đệm kết quả, vì không có cách nào để biết liệu bộ đệm có hợp lệ hay không mà không đọc document.cookielại.

(Mặc dù việc truy cập và phân tích cú pháp document.cookiessẽ chậm hơn một chút so với sử dụng bộ đệm, nhưng nó sẽ không chậm bằng việc đọc các phần khác của DOM, vì cookie không đóng vai trò trong các cây DOM / render.)


Hàm dựa trên vòng lặp

Dưới đây là câu trả lời Code Golf, dựa trên chức năng (dựa trên vòng lặp) của 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, '');
}

mà khi được rút gọn, có tới 128 ký tự (không tính tên hàm):

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

Hàm dựa trên biểu thức chính quy

Cập nhật: Nếu bạn thực sự muốn một giải pháp biểu thức chính quy:

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

Điều này thoát khỏi bất kỳ ký tự đặc biệt nào trong tên cookie trước khi xây dựng đối tượng RegExp. Tối thiểu hóa, điều này có tới 134 ký tự (không tính tên hàm):

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

Như Rudu và cwolves đã chỉ ra trong các bình luận, biểu thức thoát biểu thức chính quy có thể được rút ngắn bằng một vài ký tự. Tôi nghĩ sẽ tốt khi giữ regex thoát phù hợp (bạn có thể sử dụng nó ở nơi khác), nhưng đề xuất của họ rất đáng để xem xét.


Ghi chú

Cả hai chức năng này sẽ không xử lý nullhoặc undefined, tức là nếu có một cookie có tên "null", readCookie(null)sẽ trả về giá trị của nó. Nếu bạn cần xử lý trường hợp này, điều chỉnh mã cho phù hợp.


Lợi ích của #\sviệc kết thúc Regex thay thế của bạn là gì? Sự thay thế chung đó có thể được đơn giản hóa hơn một chút (-, không có ý nghĩa gì ngoại trừ trong ngoặc được thoát): /[[\]{}()*+?.\\^$|]/g Nó thậm chí có thể được sắp xếp với encodeURIComponent: /[()*.\\]/g
Rudu

@Rudu Regex thoát regex đó đến từ Simon Willison và cũng được thư viện XRegExp sử dụng . Nó có nghĩa là cho trường hợp chung (ví dụ: nếu bạn đang làm new RegExp('[' + str + ']')thì bạn muốn thoát ra -), vì vậy bạn có thể đúng rằng nó có thể được rút ngắn. Vì nó chỉ lưu một vài byte và việc thoát các ký tự phụ không ảnh hưởng đến biểu thức chính thức cuối cùng, nên tôi có xu hướng để nó như vậy.
Jeffery đến

@Rudu Ngoài ra tôi cũng nên tránh encodeURIComponent()trong trường hợp này vì tôi nghĩ OP đang tìm kiếm một chức năng chung có thể hoạt động với bất kỳ tên cookie nào. Nếu mã của bên thứ ba đặt cookie có tên "foo: bar", sử dụng encodeURIComponent()sẽ có nghĩa là cố gắng đọc cookie có tên "foo% 3Abar".
Jeffery đến

Tôi biết mã đến từ đâu - nó không hoàn hảo (sẽ biến a<tab>bthành a\<tab>b... đó không phải là một lối thoát hợp lệ, mặc dù vậy \<space>). Và # dường như không có ý nghĩa gì trong JS RegEx
Rudu

1
Rất nhiều thư viện phần thứ ba sử dụng encodeURIComponent, nếu một cookie được đặt tên, foo=nó sẽ được lưu trữ foo%3Dmà không cần mã hóa tên mà bạn sẽ không tìm thấy (mã của bạn sẽ không tìm thấy nó) - không có câu trả lời đúng. Nếu bạn có thể kiểm soát cách đặt cookie vào thì bạn có thể đơn giản hóa câu trả lời / đưa ra các giả định để giúp loại bỏ nó. Đơn giản nhất / tốt nhất là chỉ sử dụng a-zA-Z0-9 cho tên cookie và tránh toàn bộ encodeURIComponentvà RegExp thoát khỏi mớ hỗn độn. Nhưng bạn vẫn có thể cần phải lo lắng về decodeURIComponenttrên giá trị cookie vì đó là một thực tế khuyến cáo.
Rudu

14

mã từ google phân tích 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
}

Tôi đã thay đổi dòng cuối cùng return d[0];và sau đó tôi sử dụng if (c('EXAMPLE_CK') == null)để kiểm tra xem cookie không được xác định.
Electroid

9

Cái này thì sao?

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

Đếm 89 byte mà không có tên hàm.


Khéo léo trả lời. Đến điểm. Xứng đáng hơn upvote. kcó thể được đặt tênkey cho sự rõ ràng, nhưng dù sao.
GeroldBroser phục hồi Monica

Cảm ơn :-) Đến bây giờ, câu trả lời được chấp nhận cũng được cập nhật và cũng có câu trả lời này. Nhưng ở dạng nén thậm chí hơi nhiều hơn chỉ với 73 ký tự.
Simon Steinberger

Vì vậy, tôi đang nhắm đến sự ngắn gọn, tôi thấy. Tại sao không lập trình Câu đố & Code Golf trong danh sách trang web của bạn sau đó ... chưa? Chúc vui vẻ! Và, BTW, tôi cũng là một nhà leo núi. Berg Heil! ;)
GeroldBroser phục hồi Monica

4

Đây rồi .. Chúc mừng!

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

Lưu ý rằng tôi đã sử dụng các chuỗi mẫu của ES6 để soạn biểu thức regex.


3

cái này trong một đối tượng mà bạn có thể đọc, viết, ghi đè và xóa 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

Cả hai chức năng này trông có giá trị như nhau về mặt đọc cookie. Bạn có thể tắt một vài byte mặc dù (và nó thực sự đang xâm nhập vào lãnh thổ Code Golf tại đây):

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

Tất cả những gì tôi đã làm với điều này là thu gọn tất cả các khai báo biến thành một câu lệnh var, loại bỏ các đối số thứ hai không cần thiết trong các lệnh gọi đến chuỗi con và thay thế một lệnh gọi charAt thành một quy tắc mảng.

Điều này vẫn không ngắn như chức năng thứ hai mà bạn cung cấp, nhưng thậm chí có thể có một vài byte bị tắt:

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

Tôi đã thay đổi biểu thức con đầu tiên trong biểu thức chính quy thành biểu thức con thu được và thay đổi phần kết quả [1] thành kết quả [2] trùng với thay đổi này; cũng loại bỏ các parens không cần thiết xung quanh kết quả [2].


1

Để thực sự loại bỏ càng nhiều sự phình to càng tốt, hãy cân nhắc không sử dụng chức năng bao bọc nào cả:

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

Miễn là bạn quen thuộc với RegEx, mã đó khá sạch và dễ đọc.


0

(chỉnh sửa: đã đăng phiên bản sai trước .. và phiên bản không có chức năng tại đó. Đã cập nhật lên hiện tại, sử dụng chức năng unparam giống như ví dụ thứ hai.)

Ý tưởng tốt đẹp trong ví dụ đầu tiên cwolves. Tôi đã xây dựng trên cả hai chức năng đọc / ghi cookie khá nhỏ gọn, hoạt động trên nhiều tên miền phụ. Hình tôi muốn chia sẻ trong trường hợp bất cứ ai khác chạy qua chủ đề này để tìm kiếm điều đó.

(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)
  • Nếu bạn không vượt qua rwCookie, nó sẽ đưa tất cả cookie vào bộ lưu trữ cookie
  • Đã qua rwCookie một tên cookie, nó nhận được giá trị của cookie đó từ bộ lưu trữ
  • Đã vượt qua một giá trị cookie, nó ghi cookie và đặt giá trị đó vào bộ lưu trữ
  • Hết hạn mặc định cho phiên trừ khi bạn chỉ định một

0

Sử dụng câu trả lời của cwolves, nhưng không sử dụng hàm đóng cũng như hàm băm được tính toán trước:

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

... và giảm bớt ...

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

... bằng 127 byte.


0

Đây là giải pháp đơn giản nhất sử dụng các hàm chuỗi javascript.

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

0

Hàm sau sẽ cho phép phân biệt giữa các chuỗi rỗng và cookie không xác định. Cookie không xác định sẽ trả lại chính xác undefinedvà không phải là một chuỗi trống không giống như một số câu trả lời khác ở đây. Nhưng nó sẽ không hoạt động trên IE7 trở xuống, vì chúng không cho phép truy cập mảng vào các chỉ mục chuỗi.

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

Tại sao bạn trả lại mục thứ ba trong danh sách?
nasch

Bởi vì tôi đã không làm cho nhóm đầu tiên không bị bắt. Tôi đã cập nhật mã.
Joyce Babu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.