Soru HTML5 localStorage'da Nesnelerin Saklanması


HTML5'te bir JavaScript nesnesini saklamak istiyorum localStorageama nesnem görünüşe göre bir dizeye dönüştürülüyor.

İlkel JavaScript türlerini ve dizilerini saklayabilir ve alabilirim. localStorageama nesneler işe yaramaz. Onlar mı?

İşte kodum:

var testObject = { 'one': 1, 'two': 2, 'three': 3 };
console.log('typeof testObject: ' + typeof testObject);
console.log('testObject properties:');
for (var prop in testObject) {
    console.log('  ' + prop + ': ' + testObject[prop]);
}

// Put the object into storage
localStorage.setItem('testObject', testObject);

// Retrieve the object from storage
var retrievedObject = localStorage.getItem('testObject');

console.log('typeof retrievedObject: ' + typeof retrievedObject);
console.log('Value of retrievedObject: ' + retrievedObject);

Konsol çıkışı

typeof testObject: object
testObject properties:
  one: 1
  two: 2
  three: 3
typeof retrievedObject: string
Value of retrievedObject: [object Object]

Bana benziyor setItem yöntem, kaydetmeden önce girdiyi bir dizgeye dönüştürmektir.

Bu davranışı Safari, Chrome ve Firefox'ta görüyorum. Bu yüzden, HTML5 Web Depolama Alanı spec, tarayıcıya özel bir hata veya sınırlama değil.

Anlamayı denedim yapılandırılmış klon açıklanan algoritma http://www.w3.org/TR/html5/infrastructure.html. Ne söylediğini tam olarak anlamadım, ama belki de benim sorunum, nesnemin özellikleriyle söylenemez olmamalıdır (???)

Kolay bir çözüm var mı?


Güncelleme: W3C sonunda yapılandırılmış-klon özellikleriyle ilgili fikirlerini değiştirdi ve uygulamaları eşleşecek şekilde değiştirmeye karar verdi. Görmek https://www.w3.org/Bugs/Public/show_bug.cgi?id=12111. Yani bu soru artık% 100 geçerli değil, ancak cevaplar hala ilgi çekici olabilir.


2057
2018-01-06 04:05


Menşei


BTW, "yapılandırılmış klonlama algoritması" nı okuyorsunuz, sadece uygulamadan sonra özelliğin sadece dizi değerlerinden değiştirildiğini görüyoruz. Hata yaptım bugzilla.mozilla.org/show_bug.cgi?id=538142 bu sorunu izlemek için mozilla ile. - Nickolay
Bu indexedDB için bir iş gibi görünüyor ... - markasoftware
LocalStorage'da bir dizi Nesneyi saklamaya ne dersiniz? Dize dönüştürülmekte olan aynı sorunla karşı karşıyayım. - Jayant Pareek
Bunun yerine diziyi serileştirebilir miydin? JSON ile mağaza gibi stringify sonra yükleme üzerine tekrar ayrıştırmak? - Brandito
Kullanabilirsiniz localDataStorage javascript veri türlerini saydam bir şekilde depolamak için (Array, Boolean, Date, Float, Integer, String ve Object) - Mac


Cevaplar:


Bakıyor elma, Mozilla ve Microsoft, Belgeleme, işlevsellik sadece dize anahtar / değer çiftleri işlemek için sınırlı gibi görünüyor.

Bir geçici çözüm olabilir stringify nesneyi saklamadan önce ve daha sonra geri aldığınızda onu ayrıştırın:

var testObject = { 'one': 1, 'two': 2, 'three': 3 };

// Put the object into storage
localStorage.setItem('testObject', JSON.stringify(testObject));

// Retrieve the object from storage
var retrievedObject = localStorage.getItem('testObject');

console.log('retrievedObject: ', JSON.parse(retrievedObject));

2654
2018-01-06 04:25



Herhangi bir meta verenin kaldırılacağını gözlemleyin. Sadece anahtar-değer çiftleriyle bir nesne elde edersiniz, böylece davranışları olan herhangi bir nesnenin yeniden oluşturulması gerekir. - oligofren
@CMS, veriler kapasitenin üzerindeyse, bazı istisnalar atayabilir? - Ashish Negi
... sadece dairesel referanslı nesneler için geçerlidir, JSON.stringify() başvurulan nesneyi, dizgeselleştirdiğimiz nesnede tam olarak "içerik" (örtük olarak dizilmiş) olarak genişletir. Görmek: stackoverflow.com/a/12659424/2044940 - CoDEmanX
Bu yaklaşımla ilgili sorun, büyük dizileri veya nesneleri ele almak zorunda kalmanız durumunda performans sorunlarıdır. - Monkey King
@oligofren true, ama maja doğru bir şekilde önerilen eval () =>, bu iyi bir kullanımdan birisidir, kolayca işlev kodu alabilirsiniz => dize olarak saklayın ve sonra eval () geri dönün :) - jave.web


Bir küçük iyileştirme varyant:

Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    var value = this.getItem(key);
    return value && JSON.parse(value);
}

Nedeniyle kısa devre değerlendirmesi, getObject() irade hemen dönüş null Eğer key Depolama alanında değil. Ayrıca alamaz SyntaxError istisna varsa value olduğu "" (boş dize; JSON.parse() bunu kaldıramıyor).


563
2018-06-30 06:45



Kullanımı hemen benim için net olmadığı için hızlıca eklemek istiyorum: var userObject = { userId: 24, name: 'Jack Bauer' };  Ve onu ayarlamak içinlocalStorage.setObject('user', userObject);  Ardından depolama alanından geri aluserObject = localStorage.getObject('user');  İsterseniz bir dizi nesneyi bile saklayabilirsiniz. - zuallauz
Bu sadece boolean ifadesidir. İkinci kısım, sadece bir tanesi doğruysa değerlendirilir. Bu durumda bütün ifadenin sonucu doğru kısımdan olacaktır. Boole ifadelerinin nasıl değerlendirildiğine dayalı popüler tekniktir. - Guria
Yerel değişkenin noktasını ve kısayol değerlendirmesini burada görmüyorum (küçük performans iyileştirmeleri bir yana). Eğer key Yerel Depolamada değil window.localStorage.getItem(key) döner null - yapar değil "Yasa dışı erişim" istisnası atmak - ve JSON.parse(null) döner null de - yapar değil Ne Chromium 21 ne de ne de bir istisna atmak ES 5.1 bölüm 15.12.2, Çünkü String(null) === "null" olarak yorumlanabilir JSON literal. - PointedEars
Yerel Depolama'daki değerler her zaman ilkel dize değerleridir. Peki bu kısayol değerlendirmesinin işleyişi, birisinin ne zaman saklandığı "" (boş dizge) daha önce. Çünkü tür çevirir false ve JSON.parse(""), hangi atar ki SyntaxError istisna, çağrılmaz. - PointedEars
Bu IE8'de çalışmaz, bu yüzden desteklemeniz gerekiyorsa işlevleri doğrulanmış cevapta kullanmaktan daha iyidir. - Ezeke


Depolama nesnesini bu kullanışlı yöntemlerle genişletmek yararlı olabilir:

Storage.prototype.setObject = function(key, value) {
    this.setItem(key, JSON.stringify(value));
}

Storage.prototype.getObject = function(key) {
    return JSON.parse(this.getItem(key));
}

Bu şekilde, API'nin altında yalnızca dizeleri desteklese bile, gerçekten istediğiniz işlevi alırsınız.


196
2018-01-06 04:42



CMS'nin bir fonksiyona yaklaşması iyi bir fikirdir, sadece bir özellik testine ihtiyaç duyar: Bir tane JSON.stringify için, biri JSON.parse için ve bir tanesi de localStorage'ın gerçekten bir nesneyi ayarlayıp geri getirip getiremeyeceğini test etmek için. Ana bilgisayar nesnelerini değiştirmek iyi bir fikir değildir; Bunu ayrı bir yöntem olarak görmeyi tercih ederim. localStorage.setObject. - Garrett
Bu getObject() atar SyntaxError saklanan değer ise istisna "", Çünkü JSON.parse() bunu kaldıramıyor. Ayrıntılar için düzenleğimi Guria'nın yanıtına bakın. - PointedEars
Sadece iki sentim, ama eminim ki satıcı tarafından sağlanan nesneleri genişletmek iyi bir fikir değil. - Sethen


Depolama nesnesini genişletmek harika bir çözümdür. API'm için localStorage için bir cephe oluşturdum ve ayarlayıp aldığımda bir nesne olup olmadığını kontrol ettim.

var data = {
  set: function(key, value) {
    if (!key || !value) {return;}

    if (typeof value === "object") {
      value = JSON.stringify(value);
    }
    localStorage.setItem(key, value);
  },
  get: function(key) {
    var value = localStorage.getItem(key);

    if (!value) {return;}

    // assume it is an object that has been stringified
    if (value[0] === "{") {
      value = JSON.parse(value);
    }

    return value;
  }
}

64
2018-01-21 18:29



Bu neredeyse ihtiyacım olan şeydi. Yorumdan önce sadece (value == null) {return false} değerini eklemeliydim, aksi takdirde localStorage'da bir anahtarın varlığını kontrol ederken hatayla sonuçlandı. - Francesco Frapporti
Bu aslında oldukça güzel. @FrancescoFrapporti ile katılıyorum, null değerler için orada bir ihtiyacınız varsa. Ayrıca bir '|| değeri [0] == "[" 'orada bir dizi var. - rob_james
İyi nokta, bunu düzenleyeceğim. Null kısmına ihtiyacınız olmasa da, eğer yaparsanız üç === öneririm. JSHint veya JSLint kullanırsanız, == kullanarak uyarılırsınız. - Alex Grande
Ve ninja olmayanlar için (benim gibi), birisi bu cevap için bir kullanım örneği sağlayabilir mi? Bu mu: data.set('username': 'ifedi', 'fullname': { firstname: 'Ifedi', lastname: 'Okonkwo'});? - Ifedi Okonkwo
Evet kesinlikle! Kaşıkla beslenmek isteğimi aştığımda, test etmek için kodu aldım ve anladım. Bu cevabın harika olduğunu düşünüyorum çünkü 1) Kabul edilen cevaptan farklı olarak, dize verilerinde belirli kontrollerin yapılması zaman alır ve 2) Bir diğerinden farklı olarak, doğal bir nesneyi genişletmez. - Ifedi Okonkwo


Birçok çözümü saran büyük bir kütüphane var, böylece eski tarayıcıları bile destekliyor. jStorage

Bir nesneyi ayarlayabilirsiniz

$.jStorage.set(key, value)

Ve kolayca al

value = $.jStorage.get(key)
value = $.jStorage.get(key, "default value")

52
2017-08-23 03:52



$ yasadışı! - SuperUberDuper
@SuperUberDuper jStorage, Prototype, MooTools veya jQuery gerektirir - JProgrammer


Stringify tüm problemleri çözmez

Görünüşe göre buradaki cevaplar, JavaScript'te mümkün olan tüm türleri kapsamaz. Bu yüzden, onlarla nasıl başa çıkılacağına ilişkin bazı kısa örnekler aşağıda verilmiştir:

//Objects and Arrays:
    var obj = {key: "value"};
    localStorage.object = JSON.stringify(obj);  //Will ignore private members
    obj = JSON.parse(localStorage.object);
//Boolean:
    var bool = false;
    localStorage.bool = bool;
    bool = (localStorage.bool === "true");
//Numbers:
    var num = 42;
    localStorage.num = num;
    num = +localStorage.num;    //short for "num = parseFloat(localStorage.num);"
//Dates:
    var date = Date.now();
    localStorage.date = date;
    date = new Date(parseInt(localStorage.date));
//Regular expressions:
    var regex = /^No\.[\d]*$/i;     //usage example: "No.42".match(regex);
    localStorage.regex = regex;
    var components = localStorage.regex.match("^/(.*)/([a-z]*)$");
    regex = new RegExp(components[1], components[2]);
//Functions (not recommended):
    function func(){}
    localStorage.func = func;
    eval( localStorage.func );      //recreates the function with the name "func"

Tavsiye etmiyorum fonksiyonları saklamak için eval() kötülük güvenlik, optimizasyon ve hata ayıklama ile ilgili sorunlara yol açabilir.         Genel olarak, eval() JavaScript kodunda asla kullanılmamalıdır.

Özel üyeler

Kullanarak sorunu JSON.stringify()Nesneleri depolamak için, bu işlevin özel üyeleri serileştirememesidir. Bu sorun, üzerine yazılarak çözülebilir. .toString() yöntem (web depolama biriminde veri saklarken örtük olarak adlandırılır):

//Object with private and public members:
    function MyClass(privateContent, publicContent){
        var privateMember = privateContent || "defaultPrivateValue";
        this.publicMember = publicContent  || "defaultPublicValue";

        this.toString = function(){
            return '{"private": "' + privateMember + '", "public": "' + this.publicMember + '"}';
        };
    }
    MyClass.fromString = function(serialisedString){
        var properties = JSON.parse(serialisedString || "{}");
        return new MyClass( properties.private, properties.public );
    };
//Storing:
    var obj = new MyClass("invisible", "visible");
    localStorage.object = obj;
//Loading:
    obj = MyClass.fromString(localStorage.object);

Dairesel referanslar

Başka bir problem stringify ilgilenemezsiniz dairesel referanslar:

var obj = {};
obj["circular"] = obj;
localStorage.object = JSON.stringify(obj);  //Fails

Bu örnekte, JSON.stringify() atar TypeError  "Dairesel yapıyı JSON'a dönüştürme".         Döngüsel referansların saklanması desteklenmeli, ikinci parametre JSON.stringify() kullanılabilir:

var obj = {id: 1, sub: {}};
obj.sub["circular"] = obj;
localStorage.object = JSON.stringify( obj, function( key, value) {
    if( key == 'circular') {
        return "$ref"+value.id+"$";
    } else {
        return value;
    }
});

Bununla birlikte, dairesel referansların saklanması için etkin bir çözüm bulunması, çözülmesi gereken görevlere büyük ölçüde bağlıdır ve bu tür verilerin geri yüklenmesi de önemsiz değildir.

Bu problemle uğraşmak konusunda çoktan bir soru var: Döngüsel referanslı bir JavaScript nesnesini Stringify (JSON'a dönüştür)


50
2017-11-19 09:51





Yerel depolama için JSON nesnelerini kullanma:

// SET

var m={name:'Hero',Title:'developer'};
localStorage.setItem('us', JSON.stringify(m));

//ALMAK

var gm =JSON.parse(localStorage.getItem('us'));
console.log(gm.name);

// Tüm yerel depolama anahtarlarının ve değerlerinin yinelenmesi

for (var i = 0, len = localStorage.length; i < len; ++i) {
  console.log(localStorage.getItem(localStorage.key(i)));
}

// DELETE

localStorage.removeItem('us');
delete window.localStorage["us"];

29
2017-11-20 07:06





Teoride, nesneleri fonksiyonlarla saklamak mümkündür:

function store (a)
{
  var c = {f: {}, d: {}};
  for (var k in a)
  {
    if (a.hasOwnProperty(k) && typeof a[k] === 'function')
    {
      c.f[k] = encodeURIComponent(a[k]);
    }
  }

  c.d = a;
  var data = JSON.stringify(c);
  window.localStorage.setItem('CODE', data);
}

function restore ()
{
  var data = window.localStorage.getItem('CODE');
  data = JSON.parse(data);
  var b = data.d;

  for (var k in data.f)
  {
    if (data.f.hasOwnProperty(k))
    {
      b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");
    }
  }

  return b;
}

Ancak, işlev serileştirme / serileştirme güvenilmez çünkü uygulamaya bağımlı.


27
2018-04-05 21:20



İşlev serileştirme / serileştirme güvenilir değil çünkü uygulamaya bağımlı. Ayrıca, değiştirmek istersin c.f[k] = escape(a[k]);  Unicode güvenli c.f[k] = encodeURIComponent(a[k]); ve eval('b.' + k + ' = ' + unescape(data.f[k])); ile b[k] = eval("(" + decodeURIComponent(data.f[k]) + ")");. Parantezler gereklidir, çünkü işleviniz, eğer doğru bir şekilde serileştirilmişse, anonimdir, ki bu geçerli / Statement / gibi değildir. eval()) a atar SyntaxError istisna). - PointedEars
Ve typeof bir Şebeke, bir işlevmiş gibi yazmayın. değiştirmek typeof(a[k]) ile typeof a[k]. - PointedEars
Önerilerimi uygulamak ve yaklaşımın güvenilmezliğini vurgulamanın yanı sıra, aşağıdaki hataları düzeltdim: 1. Tüm değişkenler beyan edilmedi. 2. for-in kendi mülkleri için filtrelenmedi. 3. Referans dahil olmak üzere kod stili tutarsızdı. - PointedEars
@PointedEars hangi pratik fark yaratıyor? spec diyor the use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent.  İşlevsel farklılıklar görmüyorum. - Michael
@Michael Aldığınız bölüm ile başlar Note *in particular* that …. Ancak dönüş değeri belirtimi ile başlar An implementation-dependent representation of the function is returned. This representation has the syntax of a FunctionDeclaration. Dönüş değeri olabilir function foo () {} - varsayarak uygun uygulanması. - PointedEars


Varsayılan Depolama Birimini de geçersiz kılabilirsiniz. setItem(key,value) ve getItem(key) Nesneleri / dizileri başka bir veri türü gibi işlemek için yöntemler. Böylece sadece arayabilirsin localStorage.setItem(key,value) ve localStorage.getItem(key) normalde yaptığınız gibi.

Bu konuyu kapsamlı bir şekilde test etmedim, ama tinkering yaptığım küçük bir proje için sorunsuz bir şekilde çalıştığı görüldü.

Storage.prototype._setItem = Storage.prototype.setItem;
Storage.prototype.setItem = function(key, value)
{
  this._setItem(key, JSON.stringify(value));
}

Storage.prototype._getItem = Storage.prototype.getItem;
Storage.prototype.getItem = function(key)
{  
  try
  {
    return JSON.parse(this._getItem(key));
  }
  catch(e)
  {
    return this._getItem(key);
  }
}

22
2018-04-26 13:00



Yerel / yerleşik nesne API'lerini değiştirmek için kötü bir uygulama olarak kabul edilir. Diğer çözümler için diğer cevaplara bakın. - Schmuli
Güzel, asla spec'a bağlı herhangi bir kütüphaneyi kullanmak istemediğinizi varsayarak. - Carl Smith


Bu yazıya, bunun bir kopyası olarak kapatılmış olan başka bir yazıya isabet ettikten sonra geldim. Bu, 'localstorage'da bir dizinin nasıl saklanacağı' başlıklıydı. Hangi iş parçacığı dışında gayet normalde localStorage'da bir diziyi nasıl tutacağınız konusunda tam bir cevap verir - ancak her iki iş parçacığı içinde yer alan bilgilere dayanan bir çözüm üretmeyi başardım.

Yani, eğer birisi dizideki öğeleri itme / değiştirme / değiştirme yeteneğine sahipse ve bu dizinin localStorage'da veya gerçekten sessionStorage'da saklanmasını istiyorsa, işte şunları yapın:

Storage.prototype.getArray = function(arrayName) {
  var thisArray = [];
  var fetchArrayObject = this.getItem(arrayName);
  if (typeof fetchArrayObject !== 'undefined') {
    if (fetchArrayObject !== null) { thisArray = JSON.parse(fetchArrayObject); }
  }
  return thisArray;
}

Storage.prototype.pushArrayItem = function(arrayName,arrayItem) {
  var existingArray = this.getArray(arrayName);
  existingArray.push(arrayItem);
  this.setItem(arrayName,JSON.stringify(existingArray));
}

Storage.prototype.popArrayItem = function(arrayName) {
  var arrayItem = {};
  var existingArray = this.getArray(arrayName);
  if (existingArray.length > 0) {
    arrayItem = existingArray.pop();
    this.setItem(arrayName,JSON.stringify(existingArray));
  }
  return arrayItem;
}

Storage.prototype.shiftArrayItem = function(arrayName) {
  var arrayItem = {};
  var existingArray = this.getArray(arrayName);
  if (existingArray.length > 0) {
    arrayItem = existingArray.shift();
    this.setItem(arrayName,JSON.stringify(existingArray));
  }
  return arrayItem;
}

Storage.prototype.unshiftArrayItem = function(arrayName,arrayItem) {
  var existingArray = this.getArray(arrayName);
  existingArray.unshift(arrayItem);
  this.setItem(arrayName,JSON.stringify(existingArray));
}

Storage.prototype.deleteArray = function(arrayName) {
  this.removeItem(arrayName);
}

örnek kullanımı - localStorage dizisinde basit dizeleri depolamak:

localStorage.pushArrayItem('myArray','item one');
localStorage.pushArrayItem('myArray','item two');

örnek kullanım - sessionStorage dizisindeki nesneleri saklama:

var item1 = {}; item1.name = 'fred'; item1.age = 48;
sessionStorage.pushArrayItem('myArray',item1);

var item2 = {}; item2.name = 'dave'; item2.age = 22;
sessionStorage.pushArrayItem('myArray',item2);

dizileri işlemek için yaygın yöntemler:

.pushArrayItem(arrayName,arrayItem); -> adds an element onto end of named array
.unshiftArrayItem(arrayName,arrayItem); -> adds an element onto front of named array
.popArrayItem(arrayName); -> removes & returns last array element
.shiftArrayItem(arrayName); -> removes & returns first array element
.getArray(arrayName); -> returns entire array
.deleteArray(arrayName); -> removes entire array from storage

19
2018-05-07 11:35



Bu, localStorage veya sessionStorage'da depolanan dizileri işlemek için çok kullanışlı bir yöntem kümesidir ve çekildiğinden çok daha fazla krediyi hak eder. @Andy Lorenz Paylaşmak için zaman ayırdığınız için teşekkürler! - Velojet