Какая самая короткая функция для чтения куки по имени в JavaScript?

194

Какой самый короткий, точный и совместимый с браузером метод чтения файлов cookie в JavaScript?

Очень часто, при создании автономных скриптов (где у меня не может быть никаких внешних зависимостей), я добавляю функцию для чтения куки и обычно прибегаю кreadCookie() методу 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 и в том, чтобы решение, которое у меня есть, было действительным.

Yahel
источник
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

Некоторые замечания о подходе:

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

макинтош
источник
4
Я только что заметил, что в Firefox подход к регулярным выражениям, который я разместил выше, не так эффективен, как подход с циклическим циклом. Тесты, которые я запускал ранее, проводились в Chrome, где подход регулярных выражений работал немного лучше, чем другие подходы. Тем не менее, это все еще самый короткий, который отвечает на вопрос, который задают.
Mac
6
Почему getCookieValue(a, b)принимает параметр b?
Брент Уошборн
15
Проголосовал, но не за удобочитаемость ... Мне понадобилось время, чтобы понять, что aи как bделать.
Гиги
9
Умно, но глупо так писать, чтобы сэкономить 1 байт.
Человек
5
aПараметр не является регулярным выражением, хотя это может быть полезно, это не безопасно. Такие вещи getCookieValue('.*')вернут любое случайное печенье
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 если сделаете его однобуквенным именем функции, 90 bytesесли отпустите encodeURIComponent.

Я понял это 73 bytes, но, если честно, это 82 bytesкогда названо readCookieи 102 bytesкогда добавлено encodeURIComponent:

function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}
Марк Кан
источник
Я забыл ответное заявление, нет ничего плохого в закрытии, шиш.
Марк Кан
3
...Какой? Дифф говорит, что ты сделал. d.pr/sSte у него не было ()вызова в конце, поэтому он определял анонимную функцию, но никогда не выполнял ее.
Яхель
2
Вот, пожалуйста : jsperf.com/pre-increment-vs-post-increment Я был прав, ++ я быстрее, по крайней мере, если вы получите значение до и после. И это то, что вы делаете в цикле.
xavierm02
1
Одна важная вещь: поскольку значение «куки» кэшируется, новые куки или измененные куки из других окон или вкладок не станут видимыми. Вы можете избежать этой проблемы, сохраняя строковое значение из document.cookie в переменной и проверяя, не изменяется ли оно при каждом доступе.
Андреас
2
@StijndeWitt - как это не ответить на вопрос? 73 байта недостаточно для тебя? :) Последний ответ, который у меня есть, идентичен приведенному ниже, за исключением некоторых проверок пробелов, лол
Марк Кан,
20

Предположения

Исходя из вопроса, я считаю, что некоторые предположения / требования для этой функции включают в себя:

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

При этих предположениях ясно, что encodeURIComponent/ decodeURIComponent не следует использовать ; при этом предполагается, что код, который устанавливает cookie, также закодировал его, используя эти функции.

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

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

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


Петлевая функция

Вот ответ 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];}

Как указали в комментариях Rudu и cwolves, регулярное выражение, экранирующее регулярное выражение, может быть сокращено на несколько символов. Я думаю, что было бы хорошо, чтобы регулярное выражение оставалось последовательным (вы можете использовать его в другом месте), но их предложения стоит рассмотреть.


Ноты

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

Джеффри То
источник
В чем выгода #\sв конце вашего замены-Regex? Эту общую замену можно немного упростить (-, не имеют смысла, кроме экранированных скобок):/[[\]{}()*+?.\\^$|]/g она может быть даже encodeURIComponent/[()*.\\]/g
отсортирована
@Rudu Этот regex-escape-regex исходит от Саймона Виллисона и также используется библиотекой XRegExp . Он предназначен для общего случая (например, если вы делаете, new RegExp('[' + str + ']')то вы хотели бы убежать- ), так что вы, вероятно, правы, что его можно сократить. Так как это сохранит только несколько байтов, а экранирование лишних символов не повлияет на окончательное регулярное выражение, я склонен оставить все как есть.
Джеффри До
@Rudu Также я бы избегал encodeURIComponent()в этом случае, потому что я думаю, что OP ищет универсальную функцию, которая может работать с любым именем 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
}
ChicoDeFe
источник
Я изменил последнюю строку, 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 символами.
Саймон Стейнбергер,
Итак, вы стремитесь к краткости, я вижу. Почему тогда еще нет Programming Puzzles & Code Golf в вашем сайте ? Радоваться, веселиться! И, кстати, я тоже альпинист. Берг Хейл! ;)
Герольд Брозер
4

Здесь идет .. Ура!

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

Обратите внимание, что я использовал шаблонные строки ES6 для составления выражения регулярного выражения.

mfalade
источник
3

это в объекте, который вы можете читать, писать, перезаписывать и удалять куки.

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

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

Хорошая идея в первом примере cwolves. Я использовал обе функции для довольно компактной функции чтения / записи 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 и помещает значение в хранилище
  • По умолчанию срок действия сессии, если вы не укажете
Адам
источник
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.

document.cookie.substring(document.cookie.indexOf("COOKIE_NAME"), 
                          document.cookie.indexOf(";", 
                          document.cookie.indexOf("COOKIE_NAME"))).
  substr(COOKIE_NAME.length);
Кумар
источник
0

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

    function getCookie(name) {
      return (document.cookie.match('(?:^|;) *'+name+'=([^;]*)')||"")[1];
    }
Джойс Бабу
источник
Почему вы возвращаете третий элемент в списке?
нащий
Потому что я не сделал первую группу без захвата. Я обновил код.
Джойс Бабу