【问题标题】:Failing to call a constructor method on objects stored in the localStorage未能对存储在 localStorage 中的对象调用构造函数方法
【发布时间】:2020-11-27 13:09:46
【问题描述】:

所以我正在开发一个应用程序,它接收来自用户的数据并将其作为对象存储在数组中,然后我创建了将这些数据存储在 localStorage 中的功能。这是我正在尝试做的基本演示:

// constructor function

function Book(title, author, pages, read = false) {
  this.title = title;
  this.author = author;
  this.pages = pages;
  this.read = read;
  this.status = function() {
    console.log(this.read);
  }
}

// array to store objects

let mybooks = [];

// instances of the Book object

let book1 = new Book('Hello', 'John', 123, true);
let book2 = new Book('Hey', 'Jane', 13, false);
let book3 = new Book('Hi', 'Mary', 12, true);
let book4 = new Book('Holo', 'Peter', 10, false);
let book5 = new Book('Banda', 'Banda', 03, true);

// push the instances to the mybooks array

mybooks.push(book1);
mybooks.push(book2);
mybooks.push(book3);
mybooks.push(book4);
mybooks.push(book5);

// store mybooks to the localStorage

localStorage.mybooks = JSON.stringify(mybooks);

// retrieve data from the localStorage

let data = JSON.parse(localStorage.getItem('mybooks'));

// call the status() method on each object

data.forEach(book => {
  book.status();
});

通过在每个对象上调用 .status() 方法,我希望在控制台上获得布尔 (true/false) 值。但我得到这个错误:

Uncaught TypeError: book.status is not a function
    at app.js:36
    at Array.forEach (<anonymous>)
    at app.js:35

但如果我在将每个对象存储到 localStorage 之前对每个对象运行相同的函数,我会得到正确的输出。

【问题讨论】:

  • localStorage 存储文本,而不是对象。将数据存储在 json 中并使用存储的数据重新创建对象

标签: javascript json data-structures local-storage instance


【解决方案1】:

查看JSON.stringify ...任何有关无法转换为符合 JSON 的键值对的对象/实例的信息都将丢失。

因此,Book 实例将由一个简单的字符串表示,例如这……

'{"title":"Hello","author":"John","pages":123,"read":true}'

...如果通过JSON.parse 解析这样的字符串,则会返回一个类似...的对象

{ title: "Hello", author: "John", pages: 123, read: true }

... 它仍然可以通过它携带的数据来表示一本书,但它不能再像真正的 Book 实例那样使用,例如具有继承的 status 方法,可随时用于此类实例。

为了解决问题,OP 必须将已解析的原始书籍数据项数组(来自本地存储的数组)map 转换为真实的Book 实例数组...

function Book(title, author, pages, read = false) {
  // just a container of book specific data.
  this.title = title;
  this.author = author;
  this.pages = pages;
  this.read = read;
}
Book.prototype.status = function() {
  // `status` preferably is implemented as prototype method.
  console.log(this.read);
}


const mybooks = [
  new Book('Hello', 'John', 123, true),
  new Book('Hey', 'Jane', 13, false),
  new Book('Hi', 'Mary', 12, true),
  new Book('Holo', 'Peter', 10, false),
  new Book('Banda', 'Banda', 03, true),
];
// const [book1, book2, book3, book4, book5] = mybooks;

mybooks.forEach(book => book.status());


// localStorage.setItem('mybooks', JSON.stringify(mybooks));
const storageValue = JSON.stringify(mybooks);

console.log('storageValue :', storageValue);


// const data = JSON.parse(localStorage.getItem('mybooks'));
const storageData = JSON.parse(storageValue);

console.log('storageData ... raw book data :', storageData);

// there is no status method at neither raw book data item ...
console.log('storageData[0].status :', storageData[0].status);
console.log('storageData[4].status :', storageData[4].status);

// ... but one nevertheless can work with each raw data item ...
console.log('raw book data item `status` delegation call ...');

storageData.forEach(rawBookDataItem =>
  // explicit delegation via `call`.
  Book.prototype.status.call(rawBookDataItem)
);


// map/transform raw book data items each into a `Book` instance ...
const listOfBookInstances = storageData.map(data =>
  new Book(data.title, data.author, data.pages, data.read)
);
console.log('listOfBookInstances :', listOfBookInstances);

// call `status` method on each (new) book instance.
listOfBookInstances.forEach(book => book.status());
.as-console-wrapper { min-height: 100%!important; top: 0; }

【讨论】:

  • 嘿@Peter Seliger,非常感谢详细的反馈,使用原型是明智之举,非常感谢!
【解决方案2】:

不是存储值的方式...

localStorage.mybooks = JSON.stringify(mybooks);

改用这个...

localStorage.setItem("mybooks", JSON.stringify(mybooks));

供参考:https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem


作为第二点,使用JSON.stringifyJSON.parse 不会对函数做任何事情,只会对属性做任何事情。

因此,您需要在解析所有对象后重新分配函数。

为此,我会将status 函数从Book 函数中移出(我在下面将其称为statusFnc

然后,当您解析数据后,您可以将其分配回您的 forEach

您可以在下面看到一个工作示例,但请注意我必须注释掉 localStorage 行,因为这在 sn-p 中不起作用...

// constructor function
function Book(title, author, pages, read = false) {
  this.title = title;
  this.author = author;
  this.pages = pages;
  this.read = read;
  this.status = statusFnc;
}

// status function
let statusFnc = function() {
  console.log(this.read);
}

// array to store objects
let mybooks = [
  new Book('Hello', 'John', 123, true),
  new Book('Hey', 'Jane', 13, false),
  new Book('Hi', 'Mary', 12, true),
  new Book('Holo', 'Peter', 10, false),
  new Book('Banda', 'Banda', 03, true)
];

// store mybooks to the localStorage
//localStorage.mybooks = JSON.stringify(mybooks);
let json = JSON.stringify(mybooks);

// retrieve data from the localStorage
//let data = JSON.parse(localStorage.getItem('mybooks'));
let data = JSON.parse(json);

// reassign and call the status() method on each object
data.forEach(book => {
  book.status = statusFnc;
  book.status();
});

【讨论】:

  • 嗨@freefaller,感谢您的快速回复。但是,我尝试使用您建议的语法存储信息,但我仍然遇到同样的问题。
  • 啊,好吧,这是因为stringfy 不会转换函数……它只会处理属性。因此需要重新创建该功能。将很快更新我的答案(请给我一点时间)
  • 抱歉,已经编辑了我的答案,我不应该使用 map... 当您的 forEach 更合适时
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-11
  • 2021-02-28
  • 1970-01-01
  • 2017-07-02
  • 1970-01-01
相关资源
最近更新 更多