Qual é a função mais curta para ler um cookie por nome em JavaScript?

193

Qual é o método mais curto, preciso e compatível com vários navegadores para ler um cookie em JavaScript?

Freqüentemente, ao criar scripts independentes (onde não posso ter nenhuma dependência externa), vejo-me adicionando uma função para ler cookies e geralmente recuo no método QuirksMode.orgreadCookie() (280 bytes, 216 minificados).

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

Ele faz o trabalho, mas é feio, e acrescenta bastante inchaço a cada vez.

O método que o jQuery.cookie usa algo assim (modificado, 165 bytes, 125 minificado):

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

Observe que não é uma competição de 'Code Golf': estou legitimamente interessado em reduzir o tamanho da minha função readCookie e em garantir que a solução que tenho seja válida.

Yahel
fonte
8
Os dados de cookies armazenados são meio feios; portanto, qualquer método para lidar com eles provavelmente também será.
MVChr 12/04
4
@mVChr a sério. Em que momento foi decidido que os cookies deveriam ser acessados ​​a partir de uma string delimitada por ponto e vírgula? Quando isso foi uma boa ideia?
Yahel
7
Por que essa pergunta ainda está aberta e por que ela tem uma recompensa? Você está realmente tão desesperado para salvar talvez 5 bytes ???
Mark Kahn
cookieArr = document.cookie.split (';'). map (ck => {return {[ck.split ('=') [0] .trim ()]: ck.split ('=') [1] }})
vladimir.gorea

Respostas:

196

Mais curta, mais confiável e com melhor desempenho do que a atual resposta mais votada:

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

Uma comparação de desempenho de várias abordagens é mostrada aqui:

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

Algumas notas sobre a abordagem:

A abordagem regex não é apenas a mais rápida na maioria dos navegadores, mas também produz a função mais curta. Além disso, deve-se ressaltar que, de acordo com as especificações oficiais (RFC 2109) , o espaço após o ponto-e-vírgula que separa os cookies no document.cookie é opcional e pode-se argumentar que não deve ser invocado. Além disso, o espaço em branco é permitido antes e depois do sinal de igual (=) e pode-se argumentar que esse espaço em branco em potencial deve ser fatorado em qualquer analisador confiável de document.cookie. O regex acima é responsável por ambas as condições de espaço em branco acima.

Mac
fonte
4
Acabei de notar que, no Firefox, a abordagem regex que publiquei acima não é tão eficiente quanto a abordagem em loop. Os testes que eu executei anteriormente foram feitos no Chrome, onde a abordagem regex teve um desempenho modestamente melhor do que outras abordagens. No entanto, ainda é o mais curto que aborda a pergunta que está sendo feita.
Mac
6
Por que getCookieValue(a, b)tomar parâmetro b?
Brent Washburne
15
Upvoted, mas não para a legibilidade ... Levei um tempo para descobrir o que ae bfazer.
Gigi
9
Inteligente, mas bobo, escrever dessa maneira para economizar 1 byte.
The Muffin Man
5
aO parâmetro não possui escape de regex, embora possa ser útil, não é seguro. Coisas como getCookieValue('.*')irá devolver qualquer cookie de aleatório
Vitim.us
185

Isso só atingirá document.cookie UMA vez. Cada solicitação subsequente será instantânea.

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

Receio que realmente não exista uma maneira mais rápida do que essa lógica geral, a menos que você esteja livre para usar, .forEachdependendo do navegador (mesmo assim você não está economizando muito)

Seu próprio exemplo ligeiramente compactado para 120 bytes:

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

Você pode obtê-lo 110 bytesse criar um nome de função com uma letra, 90 bytesse você soltar oencodeURIComponent .

Eu já fiz isso 73 bytes, mas para ser justo, é 82 bytesquando nomeado readCookiee, 102 bytesem seguida, adicionando encodeURIComponent:

function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}
Mark Kahn
fonte
Eu esqueci a declaração de retorno, não há nada errado com o fechamento, sheesh.
Mark Kahn
3
...O que? diff diz que você fez. d.pr/sSte não tinha a ()chamada no final; portanto, estava definindo uma função anônima, mas nunca a executava.
Yahel
2
Aqui está: jsperf.com/pre-increment-vs-post-increment Eu estava certo, ++ i é mais rápido, pelo menos se você obtiver o valor antes e depois. E é isso que você faz em um loop for.
Xavierm02 8/11
1
Uma coisa importante: como o valor de "cookies" é armazenado em cache, novos cookies ou cookies alterados de outras janelas ou guias não se tornarão visíveis. Você pode evitar esse problema armazenando o valor da sequência de document.cookie em uma variável e verifique se ele é inalterado em cada acesso.
Andreas
2
@StijndeWitt - como isso não responde à pergunta? 73 bytes não é curto o suficiente para você? :) A última resposta que tenho é idêntica à mostrada abaixo, com exceção de algumas verificações de espaço em branco, lol
Mark Kahn
20

Premissas

Com base na pergunta, acredito que algumas suposições / requisitos para esta função incluem:

  • Ele será usado como uma função de biblioteca e, portanto, deve ser descartado em qualquer base de código;
  • Como tal, ele precisará trabalhar em muitos ambientes diferentes , ou seja, trabalhar com código JS legado, CMSes de vários níveis de qualidade, etc .;
  • Para interoperar com o código escrito por outras pessoas e / ou código que você não controla, a função não deve fazer suposições sobre como os nomes ou valores dos cookies são codificados . Chamar a função com uma string "foo:bar[0]"deve retornar um cookie (literalmente) chamado "foo: bar [0]";
  • Novos cookies podem ser escritos e / ou cookies existentes modificados a qualquer momento durante a vida útil da página.

Sob essas premissas, fica claro que encodeURIComponent/ decodeURIComponent não deve ser usado ; isso pressupõe que o código que definiu o cookie também o codificou usando essas funções.

A abordagem da expressão regular fica problemática se o nome do cookie puder conter caracteres especiais. O jQuery.cookie soluciona esse problema codificando o nome do cookie (na verdade, nome e valor) ao armazenar um cookie e decodificando o nome ao recuperar um cookie. Uma solução de expressão regular está abaixo.

A menos que você esteja apenas lendo os cookies que controla completamente, também seria aconselhável ler os cookies document.cookiediretamente e não armazenar em cache os resultados, pois não há como saber se o cache é inválido sem a leitura document.cookienovamente.

(Embora o acesso e a análise document.cookiessejam um pouco mais lentos que o uso de um cache, não seria tão lento quanto a leitura de outras partes do DOM, pois os cookies não desempenham um papel nas árvores de renderização / DOM.)


Função baseada em loop

Aqui está a resposta do Code Golf, com base na função do PPK (baseada em 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, '');
}

que quando compactado, possui 128 caracteres (sem contar o nome da função):

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

Função baseada em expressão regular

Atualização: se você realmente deseja uma solução de expressão regular:

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

Isso evita qualquer caractere especial no nome do cookie antes de construir o objeto RegExp. Minificado, trata-se de 134 caracteres (sem contar o nome da função):

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

Como Rudu e cwolves apontaram nos comentários, a expressão regular de escape de expressão regular pode ser reduzida por alguns caracteres. Eu acho que seria bom manter o regex de escape consistente (você pode usá-lo em outro lugar), mas vale a pena considerar suas sugestões.


Notas

Ambas as funções não tratam nullou undefined, ou seja, se houver um cookie chamado "null", readCookie(null)retornará seu valor. Se você precisar lidar com esse caso, adapte o código adequadamente.

Jeffery To
fonte
Qual é o benefício do #\sfinal da sua substituição-Regex? Isso genérico substituir pode ser simplificado um pouco mais (-, não têm qualquer significado exceto em suportes que são fugitivos): /[[\]{}()*+?.\\^$|]/g Pode ser ainda classificador com encodeURIComponent: /[()*.\\]/g
Rudu
@Rudu Esse regex com escape de expressões regulares vem de Simon Willison e também é usado pela biblioteca XRegExp . Ele é destinado ao caso genérico (por exemplo, se você estiver fazendo new RegExp('[' + str + ']')isso, gostaria de escapar -), então provavelmente está certo de que pode ser reduzido. Como ele salvaria apenas alguns bytes e o escape de caracteres extras não afeta o regex final, estou inclinado a deixá-lo como está.
Jeffery Até
@ Rudu Também evitaria encodeURIComponent()neste caso, porque acho que o OP está procurando por uma função genérica que possa funcionar com qualquer nome de cookie. Se o código de terceiros definir um cookie chamado "foo: bar", usar encodeURIComponent()significa tentar ler um cookie chamado "foo% 3Abar".
Jeffery até
Eu sei de onde o código vem - não é perfeito (vai se transformar a<tab>bem a\<tab>b... o que não é uma fuga válida, embora \<space>seja). E # parece não ter significado na JS RegEx
Rudu
1
Muitas bibliotecas de terceiros usam encodeURIComponent, se um cookie foi nomeado, foo=ele seria armazenado, foo%3Dsem codificar da mesma forma o nome que você não o encontrará (seu código não o encontrará) - não há resposta certa. Se você pode controlar como os cookies são inseridos, pode simplificar a resposta / fazer suposições para ajudar a divulgá-las. O mais simples / melhor é usar apenas a-zA-Z0-9 para o nome do cookie e evitar toda a encodeURIComponentbagunça escapando ao RegExp. Mas você ainda pode precisar de se preocupar com decodeURIComponenta valor do cookie já que é uma prática recomendada.
Rudu
14

código do 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
fonte
Alterei a última linha return d[0];e depois if (c('EXAMPLE_CK') == null)verifiquei se o cookie não está definido.
electroid
9

Que tal este?

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

Contados 89 bytes sem o nome da função.

Simon Steinberger
fonte
Resposta inteligente. Ao ponto. Merece mais votos positivos. kpoderia ser nomeadokey por clareza, mas de qualquer maneira.
GeroldBroser restabelece Monica
Obrigado :-) Até agora, a resposta aceita foi atualizada e contém essa também. Mas de uma forma ainda um pouco mais compactada, com apenas 73 caracteres.
amigos estão dizendo sobre simon
Então, você está apontando para a falta, eu vejo. Por que Programming Puzzles & Code Golf ainda não está na sua lista de sites ... ainda? Diverta-se! E, BTW, também sou alpinista. Berg Heil! ;)
GeroldBroser restabelece Monica
4

Aqui vai .. Saúde!

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

Observe que usei as seqüências de caracteres do modelo do ES6 para compor a expressão regex.

mfalade
fonte
3

isso em um objeto que você pode ler, escrever, sobrescrever e excluir cookies.

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

fonte
1

Ambas as funções parecem igualmente válidas em termos de leitura de cookie. No entanto, você pode economizar alguns bytes (e realmente está entrando no território do Code Golf aqui):

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

Tudo o que fiz com isso foi recolher todas as declarações de variáveis ​​em uma instrução var, remover os segundos argumentos desnecessários nas chamadas para substring e substituir a chamada charAt em uma desreferência de matriz.

Isso ainda não é tão curto quanto a segunda função que você forneceu, mas mesmo isso pode ter alguns bytes retirados:

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

Alterei a primeira subexpressão na expressão regular para ser uma subexpressão de captura e alterei a parte result [1] para result [2] para coincidir com essa alteração; também removeu as parênteses desnecessárias em torno do resultado [2].

Jeff Avallone
fonte
1

Para realmente remover o máximo de inchaço possível, considere não usar uma função de invólucro:

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

Desde que você esteja familiarizado com o RegEx, esse código é razoavelmente limpo e fácil de ler.

Abhi Beckert
fonte
0

(edit: postou a versão errada primeiro. e não funcional. Atualizada para atual, que usa uma função unparam que é muito parecida com o segundo exemplo.)

Boa ideia no primeiro exemplo cwolves. Desenvolvi os dois para uma função de leitura / gravação de cookie bastante compacta que funciona em vários subdomínios. Achei que eu iria compartilhar caso alguém mais atravesse esse segmento procurando por isso.

(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)
  • Se você passar nada do rwCookie, todos os cookies serão armazenados no armazenamento de cookies
  • Passado rwCookie um nome de cookie, ele obtém o valor desse cookie do armazenamento
  • Passou um valor de cookie, ele grava o cookie e coloca o valor no armazenamento
  • O padrão da expiração é a sessão, a menos que você especifique um
Adão
fonte
0

Usando a resposta de cwolves, mas não usando um fechamento nem um hash pré-calculado:

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

... e diminuindo ...

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

... é igual a 127 bytes.

Félix Saparelli
fonte
0

Aqui está a solução mais simples usando funções de string javascript.

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

A função a seguir permitirá a diferenciação entre cadeias vazias e cookies indefinidos. Os cookies indefinidos retornam corretamente undefinede não são uma string vazia, ao contrário de algumas das outras respostas aqui. Mas não funcionará no IE7 e abaixo, pois eles não permitem o acesso da matriz a índices de string.

    function getCookie(name) {
      return (document.cookie.match('(?:^|;) *'+name+'=([^;]*)')||"")[1];
    }
Joyce Babu
fonte
Por que você está retornando o terceiro item da lista?
nasch
Porque eu não fiz o primeiro grupo não capturar. Eu atualizei o código.
Joyce Babu