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

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

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

Ответ 1

Это только когда-либо ударит document.cookie ONE time. Каждый последующий запрос будет мгновенным.

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

Ответ 2

Более короткий, более надежный и более эффективный, чем текущий наилучший ответ:

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

Ответ 3

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

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

  • Он будет использоваться в качестве функции библиотеки и поэтому предназначен для удаления в любую кодовую базу;
  • Таким образом, он должен будет работать во многих разных средах, т.е. работать с устаревшим JS-кодом, CMS различного уровня качества и т.д.;
  • Чтобы взаимодействовать с кодом, написанным другими людьми и/или кодом, который вы не контролируете, функция не должна делать никаких предположений о том, как имена файлов cookie или значения закодированы. Вызов функции со строкой "foo:bar[0]" должен вернуть куки файл (буквально) с именем "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 (loop-based):

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, т.е. если есть куки файл с именем "null", readCookie(null) вернет его значение. Если вам необходимо обработать этот случай, соответствующим образом адаптируйте код.

Ответ 4

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

Ответ 5

Как насчет этого?

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

Считается 89 байтов без имени функции.

Ответ 6

Здесь идет.. Приветствия!

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

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

Ответ 7

Обе эти функции выглядят одинаково справедливыми с точки зрения чтения 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].

Ответ 8

это в объекте, который вы можете читать, писать, поверхWrite и удалять файлы 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;
    }
};

Ответ 9

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

Хорошая идея в первом примере 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 из хранилища
  • Передано значение cookie, оно записывает файл cookie и помещает значение в хранилище
  • Истечение срока действия по умолчанию для сеанса, если вы не укажете один

Ответ 10

Используя ответ 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];
  }
}

... и minifying...

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 байтам.

Ответ 11

Вот простейшее решение, использующее строковые функции javascript.

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

Ответ 12

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

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

Пока вы знакомы с RegEx, этот код достаточно чист и легко читается.