Яка найкоротша функція для читання файлу cookie по імені в JavaScript?


194

Який найкоротший, точний та сумісний метод перегляду веб-браузера для читання файлів cookie у JavaScript?

Дуже часто, будуючи автономні сценарії (де я не можу мати ніяких зовнішніх залежностей), я опиняюсьreadCookie() , що я додаю функцію для читання файлів cookie і, як правило, відновлююсь методом QuirksMode.org (280 байт, 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;
}

Це робить роботу, але її некрасиво, і додає зовсім небагато набряку.

Метод, що jQuery.cookie використовує щось подібне (модифіковано, 165 байт, 125 мінімізованих):

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

Зауважте, це не змагання "Code Golf": я законно зацікавлений у зменшенні розміру своєї функції readCookie, а також у забезпеченні дійсного рішення, яке я маю.


8
Збережені дані про файли cookie - це некрасиво, тому будь-який метод їх обробки, ймовірно, буде також.
mVChr

4
@mVChr серйозно. У який момент було прийнято рішення, що до файлів cookie слід звертатися з розділеною напіркою двокрапкою? Коли це коли-небудь була гарна ідея?
Яхель

7
Чому це питання все ще залишається відкритим і чому він має щедрість? Ви справді так відчайдушно заощаджуєте, можливо, 5 байт ???
Марк Кан

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

Відповіді:


198

Коротше, надійніше та ефективніше, ніж поточна найкраща відповідь:

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

Тут показано порівняння ефективності різних підходів:

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

Деякі зауваження щодо підходу:

Підхід регулярного вираження не тільки найшвидший у більшості браузерів, він дає і найкоротшу функцію. Додатково слід зазначити, що згідно з офіційною специфікацією (RFC 2109) пробіл після крапки з комою, що розділяє файли cookie у документі. Крім того, пробіл дозволений до та після знака рівності (=), і може бути зроблений аргумент, що цей потенційний пробіл слід враховувати у будь-якому надійному синтаксичному синтаксичному документі. Вищевизначення виражає обидва вищезазначені умови пробілу.


4
Я щойно помітив, що у Firefox підхід, який я розмістив вище, не є таким ефективним, як циклічний підхід. Раніше тести, які я виконував, робили в Chrome, де підхід регулярного вибору був набагато кращим, ніж інші підходи. Тим не менш, це все ще найкоротший, який вирішує питання, яке задається.
Мак

6
Чому getCookieValue(a, b)приймається параметр b?
Brent Washburne

15
Запропонований, але не для читабельності ... знадобився мені час, щоб зрозуміти, що aі що bробити.
Джіджі

9
Розумний, але дурний, щоб записати його таким чином, щоб зберегти 1 байт.
Людина-булочка

5
aПараметр не уникнув регулярного вираження, хоча він може бути корисним, але не безпечним. Такі речі getCookieValue('.*')повернуть будь-який випадковий файл cookie
Vitim.us

185

Це колись потрапить на document.cookie ОДИН раз. Кожен наступний запит буде миттєвим.

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

Боюся, що насправді немає швидшого способу, ніж ця загальна логіка, якщо ви не можете користуватися тим, .forEachщо залежить від браузера (навіть тоді ви не так багато економите)

Ваш власний приклад трохи стиснути до 120 bytes:

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

Ви можете отримати це, 110 bytesякщо ви зробите це 1-буквене ім'я функції, 90 bytesякщо ви кинете encodeURIComponent.

Я звів це до цього 73 bytes, але якщо бути справедливим, це 82 bytesколи його називають readCookieі 102 bytesколи додають encodeURIComponent:

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

Я забув декларацію про повернення, в закритті нічого поганого немає.
Марк Кан

3
...Що? diff каже, що ти зробив. d.pr/sSte у нього не було ()виклику в кінці, тому він визначав анонімну функцію, але ніколи не виконував її.
Яхель

2
Ось ви йдете: jsperf.com/pre-increment-vs-post-increment Я мав рацію, ++ i швидше, принаймні, якщо ви отримуєте значення до і після. І це ви робите в циклі for.
xavierm02

1
Одне важливе: оскільки значення "cookies" є кешованим, нові файли cookie чи змінені файли cookie з інших вікон або вкладок не стануть видимими. Ви можете уникнути цієї проблеми, зберігаючи значення рядка з document.cookie у змінній і перевіряючи, чи воно не змінюється при кожному доступі.
Андреас

2
@StijndeWitt - як це не відповідає на питання? 73 байти недостатньо короткий для вас? :) Остання відповідь у мене ідентична наведеній нижче, за винятком деяких перевірок пробілів, хаха
Марк Кан

20

Припущення

Виходячи з питання, я вважаю, що деякі припущення / вимоги до цієї функції включають:

  • Він буде використовуватися як функція бібліотеки , а значить, потрапить у будь-яку кодову базу;
  • Таким чином, йому потрібно буде працювати в багатьох різних середовищах , тобто працювати зі застарілим кодом JS, CMS різного рівня якості тощо;
  • Для взаємодії з кодом, написаним іншими людьми, та / або кодом, яким ви не керуєте, функція не повинна робити жодних припущень щодо того, як кодуються імена файлів cookie та значення . Виклик функції за допомогою рядка "foo:bar[0]"повинен повернути файл cookie (буквально) з назвою "foo: bar [0]";
  • Нові файли cookie можуть бути записані та / або існуючі файли cookie, змінені в будь-яку точку життя сторінки.

Згідно з цими припущеннями, зрозуміло, що encodeURIComponent/ decodeURIComponent не слід використовувати ; при цьому передбачається, що код, який встановлює файл cookie, також кодував його за допомогою цих функцій.

Підхід до регулярного вираження стає проблематичним, якщо ім'я файлу cookie може містити спеціальні символи. jQuery.cookie вирішує цю проблему, кодуючи ім'я файлу cookie (власне і ім’я, і значення) під час зберігання файлу cookie, і декодує ім'я під час отримання файлу cookie. Розчин регулярного вираження нижче.

Якщо ви лише читаєте файли cookie, якими ви керуєте повністю, також було б доцільно читати файли cookie document.cookieбезпосередньо та не кешувати результати, оскільки немає способу дізнатися, чи кеш недійсний без document.cookieповторного читання .

(Хоча доступ і аналіз document.cookiesбудуть трохи повільнішими, ніж використання кешу, це буде не так повільно, як читання інших частин DOM, оскільки файли cookie не грають ролі в DOM / візуалізації дерев.)


Функція на основі циклу

Ось відповідь Code Golf на основі функції (на основі циклу) 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, '');
}

який при зміні доходить до 128 символів (не рахуючи назви функції):

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

Регулярна функція на основі вираження

Оновлення: якщо ви дійсно хочете звичайного рішення виразу:

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

Це уникає будь-яких спеціальних символів у назві файлів cookie перед побудовою об'єкта RegExp. У мінімізованому вигляді це до 134 символів (не рахуючи назви функції):

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

Як в коментарях зазначали Руду та каульви, регулярний вираз, що виходить з виразів, може бути скорочений на кілька символів. Я думаю, що було б добре підтримувати регулярний регулярний вираз (ви можете використовувати його в іншому місці), але їх пропозиції варто розглянути.


Примітки

Обидві ці функції не буде обробляти nullабо undefined, наприклад , якщо є печиво під назвою «нуль», readCookie(null)поверне його значення. Якщо вам потрібно розібратися в цьому випадку, адаптуйте код відповідно.


Яка вигода #\sв кінці вашого заміни-Regex? Ця загальна заміна може бути спрощена трохи далі (-, не має сенсу, окрім як у дужках, які вийшли): /[[\]{}()*+?.\\^$|]/g Це може бути навіть сортування з encodeURIComponent: /[()*.\\]/g
Rudu

@Rudu Цей регулярно витікаючий регулярний вираз походить від Саймона Віллісона, а також використовується бібліотекою XRegExp . Він призначений для загального випадку (наприклад, якщо ви робите, new RegExp('[' + str + ']')то хотіли б втекти -), тому ви, мабуть, праві, що його можна скоротити. Оскільки це дозволить зберегти лише кілька байт, а уникнення зайвих символів не впливає на остаточний регулярний вираз, я схильний залишити його таким, яким він є.
Джеффірі

@Rudu Також я б уникав encodeURIComponent()цього випадку, тому що я думаю, що ОП шукає загальну функцію, яка може працювати з будь-яким ім'ям файлів cookie. Якщо стороннім кодом встановлено файл cookie під назвою "foo: bar", використання encodeURIComponent()буде означати спробу прочитати файл cookie під назвою "foo% 3Abar".
Джеффірі

Я знаю, звідки береться код - він не ідеальний (перетвориться a<tab>bна a\<tab>b... що не є дійсним втечею, хоча \<space>є). І #, здається, не має сенсу в JS RegEx
Руду,

1
Багато бібліотек третьої частини використовують encodeURIComponent, якщо cookie було названо, foo=воно зберігатиметься, оскільки foo%3Dбез кодування імені ви його не знайдете (ваш код не знайде) - правильної відповіді немає. Якщо ви можете контролювати спосіб розміщення файлів cookie, ви можете спростити відповідь / зробити припущення, щоб допомогти вийти з нього. Найпростіше / найкраще - використовувати лише a-zA-Z0-9 для імені файлів cookie та уникати цілого encodeURIComponentі RegExp, що виходить з ладу. Але вам все одно доведеться турбуватися про decodeURIComponentна значенні печива , так як це рекомендована практика.
Руду

14

код з 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
}

Я змінив останній рядок, return d[0];а потім if (c('EXAMPLE_CK') == null)перевіряв, чи cookie не визначено.
електроїд

9

Як щодо цього?

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

Нараховується 89 байт без назви функції.


Розумна відповідь. До суті. Заслуговує більшої кількості нагород. kможна було назватиkey для зрозумілості, але все одно.
ГерольдБрозер повертає Моніку

Дякую :-) На сьогодні прийнята відповідь була оновлена ​​і містить і цю. Але в ще трохи стисненому вигляді з лише 73 символами.
Саймон Штейнбергер

Отже, ви прагнете короткості, я бачу. Чому програмування головоломок та код-гольфу тоді у вашому списку сайтів ще ... Веселіться! І, до речі, я теж альпініст. Берг Хайль! ;)
ГерольдБрозер повертає Моніку

4

Ось іде .. Ура!

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

Зауважте, що я використав рядки шаблонів ES6, щоб скласти вираз регулярного вираження.


3

це в об'єкті, який ви можете читати, писати, перезаписувати та видаляти файли 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

Обидві ці функції виглядають однаково справедливими щодо читання файлів cookie. Ви можете поголити кілька байтів (і він дійсно потрапляє на територію 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;
}

Все, що я зробив з цим, - це згортання всіх змінних оголошень в один оператор var, видалення непотрібних другого аргументу у викликах до підрядків та заміна одного виклику charAt на відмітку масиву.

Це все ще не так коротко, як друга функція, яку ви надали, але навіть вона може мати кілька байтів:

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

Я змінив перший підвираз у регулярному виразі на захоплюючий підвираз і змінив результат [1] на результат [2], щоб він збігався з цією зміною; також видалено непотрібні паролі навколо результату [2].


1

Щоб по-справжньому видалити якомога більше набряку, рекомендуйте взагалі не використовувати функцію обгортки:

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

Поки ви знайомі з RegEx, цей код досить чистий і легкий для читання.


0

(Редагувати: спочатку розміщена неправильна версія .. і нефункціональна у цій. Оновлена ​​до поточної, яка використовує функцію непарамування, яка схожа на другий приклад.)

Приємна ідея в першому прикладі ковтажків. Я базувався на досить компактній функції читання / запису файлів cookie, яка працює в декількох субдоменах. Думаю, я б ділюся на випадок, якщо хтось інший зіткнеться з цією ниткою і шукає цього.

(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)
  • Якщо ви нічого не передасте rwCookie, воно отримає всі файли cookie у сховищі файлів cookie
  • Передавши rwCookie ім'я файлу cookie, воно отримує значення цього файлу cookie з пам’яті
  • Після переданого значення файлу cookie він записує файл cookie та розміщує його у сховищі
  • За замовчуванням термін дії сеансу, якщо ви не вказали його

0

Використовуючи відповідь cwolves, але не використовуючи закриття чи попередньо обчислений хеш:

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

... і мінімізуючи ...

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

... дорівнює 127 байт.


0

Ось найпростіше рішення з використанням функцій javascript JavaScript.

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

0

Наступна функція дозволить розмежувати порожні рядки та невизначені файли cookie. Не визначені файли cookie будуть правильно повертатися, undefinedа не порожнім рядком на відміну від деяких інших відповідей тут. Але він не буде працювати в IE7 і нижче, оскільки вони не дозволяють отримати масив до рядкових індексів.

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

Чому ви повертаєте третій елемент у списку?
нач

Тому що я не зробив першу групу незахопленням. Я оновив код.
Джойс Бабу
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.