使用 setItem 方法或使用属性访问器会改变 localStorage 的反应方式。
这是一个错误吗?
看起来不像。从规格:
当 setItem()、removeItem() 和 clear() 方法在与本地存储区域关联的 Storage 对象 x 上调用时,如果这些方法执行了某些操作,则在每个 Document 对象中,其 Window 对象的 localStorage 属性的 Storage 对象为与除 x 之外的相同存储区域相关联,必须触发存储事件,如下所述。
localStorage 对象是一个IDL 属性,具有对 Storage 接口的引用。Storage 接口扩展了 Object.prototype,它带来了额外的属性,并且还定义了自己的一组属性。
该setItem
方法将属性存储在存储列表中。当对象属性发生变化时,对象也会触发一个事件,并在这些情况下更新存储区域。
所以预期的行为setItem
是将键值对存储在存储列表中。的预期行为getItem
是从存储列表中提取与键关联的值。
那么当我们尝试直接访问属性时,预期的行为是什么?此行表示键值列表充当“支持的属性”:
Storage 对象上支持的属性名称是当前存在于与该对象关联的列表中的每个键/值对的键。
这是什么意思?这意味着这些属性被映射到接口规范定义的 getter 和 setter。对于存储,接口规范如下所示:
interface Storage {
readonly attribute unsigned long length;
DOMString? key(unsigned long index);
getter DOMString getItem(DOMString key);
setter creator void setItem(DOMString key, DOMString value);
deleter void removeItem(DOMString key);
void clear();
};
SolocalStorage["x"]
映射到 的值getItem("x")
,它返回存储列表中“x”的值,localStorage["x"] = y
并将映射到setItem("x",y)
。
但是,只有在对象上没有该名称的本机属性时才会运行这些,除非在接口上设置了overridebuiltins属性(它不适用于存储)。
如果 [OverrideBuiltins] 扩展属性出现在接口上,则表明对于实现该接口的平台对象,无论对象上存在哪些其他属性,与该对象支持的所有属性名称对应的属性都将出现在该对象上或其原型链。这意味着命名属性将始终遮盖任何原本会出现在对象上的属性。这与通常的行为形成对比,通常的行为是仅当对象本身或其原型链上的某处没有同名的属性时才公开命名属性。
因此,对于属性,我们应该期望,如果在对象上定义了一个函数,它将始终在请求时访问/设置它。否则它将检查存储列表并查看它是否具有值并更改或返回它。
摘要(TL;DR)
规范中的预期行为,以及我们在您测试时发现的行为是setItem
将getItem
预先存在的属性/函数的值存储为键/值对,并访问它们,而不会覆盖现有值。当我们尝试直接通过 localStorage["getItem"] 访问或修改这些值时,我们将改为访问本地属性,因为“支持的属性”规范要求默认情况下不覆盖内置属性。