这是 Indexed Database API 的第三版。第一版简称为“Indexed Database API”,于 2015 年 1 月 8 日成为 W3C 推荐标准。第二版标题为“Indexed Database API 2.0”,于 2018 年 1 月 30 日成为 W3C 推荐标准。
Indexed Database API 3.0 旨在取代 Indexed Database API 2.0。
1. 引言
用户代理需要在本地存储大量对象,以满足 Web 应用的离线数据需求。[WEBSTORAGE] 对于存储键值对非常有用。然而,它不支持键的有序检索、值的有效搜索,或同一个键存储多个重复值。
本规范提供了一个具体的 API,用于执行大多数复杂查询处理器核心中的高级键值数据管理。它通过使用事务性数据库来存储键及其对应的值(每个键对应一个或多个),并提供了一种按确定顺序遍历键的方法。这通常通过使用持久化 B 树数据结构来实现,该结构在插入、删除以及遍历海量数据记录方面被认为是高效的。
"library" 的数据库。它拥有一个 "books" 对象仓库,用于按 "isbn" 属性(作为主键)存储书籍记录。书籍记录具有 "title" 属性。本示例人为地要求书籍标题必须唯一。代码通过设置 unique 选项创建名为 "by_title" 的索引来强制执行此操作。此索引用于按标题查找书籍,并将阻止添加标题重复的书籍。
书籍记录还有一个 "author" 属性,不要求唯一。代码创建了另一个名为 "by_author" 的索引,以允许按该属性进行查找。
代码首先打开数据库连接。upgradeneeded 事件处理器代码会在需要时创建对象仓库和索引。success 事件处理器代码会保存打开的连接,以供后续示例使用。
const request= indexedDB. open( "library" ); let db; request. onupgradeneeded= function () { // The database did not previously exist, so create object stores and indexes. const db= request. result; const store= db. createObjectStore( "books" , { keyPath: "isbn" }); const titleIndex= store. createIndex( "by_title" , "title" , { unique: true }); const authorIndex= store. createIndex( "by_author" , "author" ); // Populate with initial data. store. put({ title: "Quarry Memories" , author: "Fred" , isbn: 123456 }); store. put({ title: "Water Buffaloes" , author: "Fred" , isbn: 234567 }); store. put({ title: "Bedrock Nights" , author: "Barney" , isbn: 345678 }); }; request. onsuccess= function () { db= request. result; };
以下示例使用事务填充数据库。
const tx= db. transaction( "books" , "readwrite" ); const store= tx. objectStore( "books" ); store. put({ title: "Quarry Memories" , author: "Fred" , isbn: 123456 }); store. put({ title: "Water Buffaloes" , author: "Fred" , isbn: 234567 }); store. put({ title: "Bedrock Nights" , author: "Barney" , isbn: 345678 }); tx. oncomplete= function () { // All requests have succeeded and the transaction has committed. };
以下示例使用索引在数据库中按标题查找单本书籍。
const tx= db. transaction( "books" , "readonly" ); const store= tx. objectStore( "books" ); const index= store. index( "by_title" ); const request= index. get( "Bedrock Nights" ); request. onsuccess= function () { const matching= request. result; if ( matching!== undefined ) { // A match was found. report( matching. isbn, matching. title, matching. author); } else { // No match was found. report( null ); } };
以下示例使用索引和游标在数据库中按作者查找所有书籍。
const tx= db. transaction( "books" , "readonly" ); const store= tx. objectStore( "books" ); const index= store. index( "by_author" ); const request= index. openCursor( IDBKeyRange. only( "Fred" )); request. onsuccess= function () { const cursor= request. result; if ( cursor) { // Called for each matching record. report( cursor. value. isbn, cursor. value. title, cursor. value. author); cursor. continue (); } else { // No more matching records. report( null ); } };
以下示例展示了请求失败时处理错误的一种方式。
const tx= db. transaction( "books" , "readwrite" ); const store= tx. objectStore( "books" ); const request= store. put({ title: "Water Buffaloes" , author: "Slate" , isbn: 987654 }); request. onerror= function ( event) { // The uniqueness constraint of the "by_title" index failed. report( request. error); // Could call event.preventDefault() to prevent the transaction from aborting. }; tx. onabort= function () { // Otherwise the transaction will automatically abort due the failed request. report( tx. error); };
当不再需要数据库连接时,可以将其关闭。
db. close();
将来,数据库可能会增长并包含其他对象仓库和索引。以下示例展示了处理旧版本迁移的一种方式。
const request= indexedDB. open( "library" , 3 ); // Request version 3. let db; request. onupgradeneeded= function ( event) { const db= request. result; if ( event. oldVersion< 1 ) { // Version 1 is the first version of the database. const store= db. createObjectStore( "books" , { keyPath: "isbn" }); const titleIndex= store. createIndex( "by_title" , "title" , { unique: true }); const authorIndex= store. createIndex( "by_author" , "author" ); } if ( event. oldVersion< 2 ) { // Version 2 introduces a new index of books by year. const bookStore= request. transaction. objectStore( "books" ); const yearIndex= bookStore. createIndex( "by_year" , "year" ); } if ( event. oldVersion< 3 ) { // Version 3 introduces a new object store for magazines with two indexes. const magazines= db. createObjectStore( "magazines" ); const publisherIndex= magazines. createIndex( "by_publisher" , "publisher" ); const frequencyIndex= magazines. createIndex( "by_frequency" , "frequency" ); } }; request. onsuccess= function () { db= request. result; // db.version will be 3. };
upgradeneeded 事件),则在所有其他客户端关闭其与当前数据库版本的连接之前,它无法进行升级。为了避免阻塞新客户端的升级,客户端可以监听 versionchange 事件。当另一个客户端想要升级数据库时,该事件会触发。为了允许升级继续,请通过采取最终关闭该客户端与数据库的连接的操作来响应 versionchange 事件。
执行此操作的一种方法是重新加载页面。
db. onversionchange= function () { // First, save any unsaved data: saveUnsavedData(). then( function () { // If the document isn't being actively used, it could be appropriate to reload // the page without the user's interaction. if ( ! document. hasFocus()) { location. reload(); // Reloading will close the database, and also reload with the new JavaScript // and database definitions. } else { // If the document has focus, it can be too disruptive to reload the page. // Maybe ask the user to do it manually: displayMessage( "Please reload this page for the latest version." ); } }); }; function saveUnsavedData() { // How you do this depends on your app. } function displayMessage() { // Show a non-modal message to the user. }
另一种方法是调用连接的 close() 方法。但是,你需要确保你的应用意识到这一点,因为后续访问数据库的尝试将会失败。
db. onversionchange= function () { saveUnsavedData(). then( function () { db. close(); stopUsingTheDatabase(); }); }; function stopUsingTheDatabase() { // Put the app into a state where it no longer uses the database. }
新客户端(尝试升级的那个)可以使用 blocked 事件来检测是否有其他客户端正在阻止升级发生。如果其他客户端在它们的 versionchange 事件触发后仍然持有数据库连接,则会触发 blocked 事件。
const request= indexedDB. open( "library" , 4 ); // Request version 4. let blockedTimeout; request. onblocked= function () { // Give the other clients time to save data asynchronously. blockedTimeout= setTimeout( function () { displayMessage( "Upgrade blocked - Please close other tabs displaying this site." ); }, 1000 ); }; request. onupgradeneeded= function ( event) { clearTimeout( blockedTimeout); hideMessage(); // ... }; function hideMessage() { // Hide a previously displayed message. }
只有当另一个客户端未能从数据库断开连接时,用户才会看到上述消息。理想情况下,用户永远不会看到这个。
2. 构造
名称 (Name) 是等同于 DOMString 的字符串;即任意长度的 16 位代码单元序列,包括空字符串。名称总是作为不透明的 16 位代码单元序列进行比较。
如果实现使用的存储机制不支持任意字符串,实现可以使用转义机制或类似方法将提供的名称映射为它可以存储的字符串。
要从列表 names 创建排序名称列表,请执行以下步骤:
-
返回一个与 sorted 关联的新
DOMStringList。
详细信息
这匹配sort() 方法在 String 的 Array 上的表现。这种排序方式比较每个字符串中的 16 位代码单元,产生高效、一致且确定的排序顺序。产生的列表不会匹配任何特定的字母表或词典顺序,特别是对于由代理对表示的代码点而言。2.1. 数据库
每个存储键都关联有一组数据库。数据库拥有零个或多个对象仓库,用于保存数据库中存储的数据。
数据库有一个名称,用于在特定的存储键内标识它。该名称是一个名称,在数据库的整个生命周期内保持不变。
注意: 每个数据库在同一时间只有一个版本;数据库不能同时存在多个版本。更改版本的唯一方法是使用升级事务。
数据库最多关联有一个升级事务,该事务或者是空值,或者是升级事务,初始时为空值。
2.1.1. 数据库连接
脚本不直接与数据库交互。相反,脚本通过连接间接访问。一个连接对象可用于操作该数据库中的对象。这也是获取该数据库事务的唯一方式。
打开数据库的行为会创建一个连接。在任何给定时间,可能存在多个指向特定数据库的连接。
连接只能访问与打开该连接的全局作用域的存储键相关联的数据库。
注意: 这不受 Document 的 domain 更改的影响。
连接有一个版本,在连接创建时设置。除非中止升级(在这种情况下,它会被设置为数据库的前一个版本),否则它在连接的整个生命周期内保持不变。一旦连接关闭,版本就不会再改变。
每个连接都有一个关闭挂起标志,初始为 false。
当连接首次创建时,它处于打开状态。连接可以通过多种方式关闭。如果创建连接的执行上下文被销毁(例如,由于用户导航离开该页面),连接就会关闭。连接也可以通过关闭数据库连接的步骤显式关闭。当连接关闭时,如果其关闭挂起标志尚未为 true,则总会被设置为 true。
在特殊情况下,用户代理可能会关闭连接,例如由于失去了文件系统访问权限、权限变更或存储键的存储空间被清除。如果发生这种情况,用户代理必须使用连接并设置 强制标志 为 true,运行关闭数据库连接算法。
连接拥有一个对象仓库集合,该集合在连接创建时被初始化为关联数据库中对象仓库的集合。集合的内容将保持不变,除非升级事务处于活动状态。
如果尝试升级或删除数据库,将在打开的连接上触发类型为 versionchange 的事件。这让连接有机会关闭以允许升级或删除继续进行。
2.2. 对象仓库
对象仓库是在数据库中存储数据的主要机制。
每个数据库都有一组对象仓库。对象仓库的集合可以被更改,但仅能使用升级事务,即响应 upgradeneeded 事件时。当创建新数据库时,它不包含任何对象仓库。
对象仓库拥有一个记录列表,用于保存对象仓库中的数据。每条记录由一个键和一个值组成。列表根据键按升序排列。在同一个对象仓库中,不可能存在多条具有相同键的记录。
对象仓库有一个名称,即一个名称。在任何时候,该名称在所属的数据库内都是唯一的。
对象仓库可选拥有一个键路径。如果对象仓库拥有键路径,则称其使用内联键。否则,称其使用离线键。
2.2.1. 对象仓库句柄
脚本不直接与对象仓库交互。相反,在事务中,脚本通过对象仓库句柄间接访问。
对象仓库句柄拥有一个关联的对象仓库和一个关联的事务。多个句柄可以与不同事务中的同一个对象仓库相关联,但在同一个事务内,只能有一个与特定对象仓库相关联的对象仓库句柄。
对象仓库句柄拥有一个索引集合,在对象仓库句柄创建时,初始化为引用关联对象仓库的索引集合。集合的内容将保持不变,除非升级事务处于活动状态。
2.3. 值
每条记录都与一个值关联。用户代理必须支持任何可序列化对象。这包括诸如 String 原始值和 Date 对象等简单类型,以及 Object 和 Array 实例、File 对象、Blob 对象、ImageData 对象等。记录值通过值而非引用进行存储和检索;随后对值进行的更改对数据库中存储的记录没有影响。
记录值是 StructuredSerializeForStorage 操作输出的记录。
2.4. 键
为了有效地检索存储在索引数据库中的记录,每条记录均按照其键进行组织。
键拥有一个关联的类型,为以下之一:数字、日期、字符串、二进制或数组。
键还有一个关联的值,若类型为数字或日期,则为 unrestricted double;若类型为字符串,则为 DOMString;若类型为二进制,则为字节序列;若类型为数组,则为其他键的列表。
ECMAScript [ECMA-262] 值可以通过遵循将值转换为键的步骤转换为键。
-
Number原始值(NaN 除外)。这包括 Infinity 和 -Infinity。 -
Date对象([[DateValue]] 内部槽为 NaN 的情况除外)。 -
String原始值。 -
ArrayBuffer对象(或缓冲区的视图,如Uint8Array)。 -
Array对象,其中每个项目都被定义、本身是一个有效的键,且没有直接或间接包含其自身。这包括空数组。数组可以包含其他数组。
尝试将其他 ECMAScript 值转换为键将会失败。
要比较两个键 a 和 b,请执行以下步骤:
-
令 ta 为 a 的类型。
-
令 tb 为 b 的类型。
-
如果 ta 不等于 tb,则执行以下步骤:
-
如果 ta 为数组,则返回 1。
-
如果 tb 为数组,则返回 -1。
-
如果 ta 为二进制,则返回 1。
-
如果 tb 为二进制,则返回 -1。
-
如果 ta 为字符串,则返回 1。
-
如果 tb 为字符串,则返回 -1。
-
如果 ta 为日期,则返回 1。
-
断言:tb 为日期。
-
返回 -1。
-
-
令 va 为 a 的值。
-
令 vb 为 b 的值。
-
在 ta 上切换:
- number
- 日期
-
-
如果 va 大于 vb,则返回 1。
-
如果 va 小于 vb,则返回 -1。
-
返回 0。
-
- string
- 二进制:
- array
如果比较两个键 a 和 b 的结果为 1,则称键 a 大于键 b。
如果比较两个键 a 和 b 的结果为 -1,则称键 a 小于键 b。
如果比较两个键 a 和 b 的结果为 0,则称键 a 等于键 b。
注意: 根据上述规则,负无穷大是键的最小值。数字键小于日期键。日期键小于字符串键。字符串键小于二进制键。二进制键小于数组键。不存在最大键值。这是因为由任何潜在的最大键后跟另一个键组成的数组更大。
注意: 二进制键的成员作为无符号字节值(在 0 到 255 范围内,含边界)进行比较,而非有符号 byte 值(在 -128 到 127 范围内,含边界)。
2.5. 键路径
键路径是一个定义如何从值中提取键的字符串或字符串列表。有效键路径为以下之一:
-
一个空字符串。
-
一个标识符,即匹配 ECMAScript 语言规范 [ECMA-262] 中 IdentifierName 产生式的字符串。
-
一个由两个或多个标识符组成的字符串,由句点(U+002E FULL STOP)分隔。
-
一个仅包含符合上述要求的字符串的非空列表。
注意: 键路径中不允许有空格。
键路径值只能从由 StructuredSerializeForStorage 显式复制的属性中访问,以及以下特定类型属性:
| 类型 | 属性 |
|---|---|
Blob
| size, type |
File
| name, lastModified |
Array
| length
|
字符串 (String)
| length
|
2.6. 索引
有时,通过除键之外的其他方式检索对象仓库中的记录会很有用。索引允许使用对象仓库记录中值的属性,在对象仓库中查找记录。
索引是一种专门的持久化键值存储,并具有一个引用对象仓库。索引具有一个记录列表,用于保存索引中存储的数据。索引中的记录会在插入、更新或删除引用的对象仓库中的记录时自动填充。可以有多个索引引用同一个对象仓库,对象仓库中的更改会导致所有此类索引得到更新。
索引记录中的值始终是索引引用的对象仓库中键的值。键是使用键路径从引用对象仓库的值中导出的。如果索引引用的对象仓库中给定的键为 X 的记录具有值 A,并且在 A 上评估索引的键路径得出结果 Y,则索引将包含一条键为 Y、值为 X 的记录。
123、值为 { name: "Alice", title: "CEO" } 的记录,并且索引的键路径为 "name",则索引将包含一条键为 "Alice"、值为 123 的记录。索引中的记录被称为拥有引用值。这是索引引用的对象仓库中具有与索引记录的值相等的键的记录的值。因此,在上面的示例中,索引中键为 Y、值为 X 的记录具有 A 的引用值。
注意: 索引中的每条记录仅引用索引引用的对象仓库中的一条记录。但是,索引中可能有多个记录引用对象仓库中的同一条记录。也可能索引中没有任何记录引用对象仓库中的给定记录。
索引中的记录始终按照记录的键排序。然而,与对象仓库不同,给定索引可以包含多个具有相同键的记录。此类记录会根据索引记录的值(即引用对象仓库中记录的键)进行进一步排序。
索引有一个名称,即一个名称。在任何时候,该名称在索引引用的对象仓库内都是唯一的。
索引有一个唯一性标志。当为 true 时,索引强制要求索引中没有两条记录具有相同的键。如果试图插入或修改索引引用的对象仓库中的记录,使得在记录的新值上评估索引的键路径产生的结果已存在于索引中,则对对象仓库的修改尝试失败。
索引有一个多条目标志。此标志影响当评估索引的键路径结果为数组键时索引的行为。如果其多条目标志为 false,则会将一条记录(其键为数组键)添加到索引中。如果其多条目标志为 true,则会为每个子键向索引中添加一条记录。
2.6.1. 索引句柄
脚本不直接与索引交互。相反,在事务中,脚本通过索引句柄间接访问。
2.7. 事务
事务用于与数据库中的数据交互。无论何时读取或写入数据库数据,都是通过使用事务来完成的。
事务提供了一些防止应用和系统故障的保护。事务可用于存储多条数据记录或有条件地修改某些数据记录。事务代表了一组原子且持久的数据访问和数据变更操作。
所有事务都通过连接创建,该连接即事务的连接。
如果任何对象仓库同时存在于两个事务的作用域中,则这两个事务具有重叠作用域。
事务有一个模式,决定了可以在该事务上执行哪些类型的交互。模式在事务创建时设置,并在事务的整个生命周期内保持固定。事务的模式为以下之一:
- "
readonly" -
该事务仅允许读取数据。此类事务无法进行任何修改。其优势在于,即使作用域重叠(即使用相同的对象仓库),也可以同时启动多个只读事务。此类事务可以在数据库打开后的任何时间创建。
- "
readwrite" -
该事务允许从现有对象仓库中读取、修改和删除数据。但是,不能添加或删除对象仓库和索引。如果多个 "
readwrite" 事务的作用域重叠,则它们不能同时启动,因为这意味着它们可能在事务执行过程中修改彼此的数据。此类事务可以在数据库打开后的任何时间创建。 - "
versionchange" -
该事务允许从现有对象仓库中读取、修改和删除数据,还可以创建和删除对象仓库和索引。它是唯一可以进行此类操作的事务类型。此类事务无法手动创建,而是在触发
upgradeneeded事件时自动创建。
事务拥有一个持久性提示。这是一个给予用户代理的提示,指示在提交事务时是优先考虑性能还是持久性。持久性提示为以下之一:
strict" 是提示用户代理在触发 complete 事件之前刷新任何操作系统 I/O 缓冲区的提示。虽然这提供了更高的可靠性以确保在随后的操作系统崩溃或断电情况下更改会被持久化,但刷新缓冲区可能需要大量时间并消耗移动设备的电量。鼓励 Web 应用对缓存或快速更改的记录等短暂数据使用 "relaxed",并在降低数据丢失风险的重要性超过性能和功耗影响的情况下使用 "strict"。鼓励实现在平衡应用提供的持久性提示与对用户和设备的影响之间权衡。
abort() 设置的。2.7.1. 事务生命周期
事务拥有一个状态,该状态为以下之一:
- active
-
当事务首次被创建时,以及在分发与该事务关联的请求的事件期间,事务处于此状态。
当事务处于此状态时,可以针对该事务发起新的请求。
- inactive (不活跃)
-
当事务创建后控制权返回事件循环,且未进行事件分发时,事务处于此状态。
当事务处于此状态时,不能针对该事务发起任何请求。
- 正在提交 (committing)
-
一旦与事务关联的所有请求完成,事务在尝试提交时将进入此状态。
当事务处于此状态时,不能针对该事务发起任何请求。
- finished (已完成)
-
一旦事务提交或中止,它将进入此状态。
当事务处于此状态时,不能针对该事务发起任何请求。
事务应该是短生命周期的。下述的自动提交功能鼓励这样做。
注意:作者仍可能使事务长期存活;然而,不建议使用此模式,因为它可能导致糟糕的用户体验。
事务的生命周期如下:
-
事务在作用域和模式下被创建。当事务被创建时,其状态初始为活跃 (active)。
-
当实现能够强制执行事务的作用域和模式约束(定义在下文)时,实现必须加入一个数据库任务队列以异步启动该事务。
一旦事务被启动,实现即可开始执行针对该事务的请求。请求必须按照针对该事务发起的顺序执行。同样,它们的结果必须按照特定事务中发起请求的顺序返回。对于不同事务中的请求结果返回顺序没有保证。
注意:事务模式确保针对不同事务发起的两个请求可以以任何顺序执行,而不会影响存储在数据库中的最终数据。
-
当与事务关联的每个请求被处理时,将触发一个
success或error事件。在分发事件期间,事务状态被设置为活跃,允许发起额外的请求。事件分发完成后,事务状态再次被设置为不活跃 (inactive)。 -
事务可以在其完成前的任何时刻被中止 (abort),即使该事务当前未处于活跃状态或尚未启动。
对
abort()的显式调用将发起一次中止。在未被脚本处理的请求失败后,也会发起中止。当事务中止时,实现必须撤销(回滚)在该事务期间对数据库所做的任何更改。这包括对对象存储内容的更改,以及对对象存储和索引的添加和移除。
-
当针对事务发起的所有请求已完成且其返回结果已处理,没有针对事务发起新的请求,且事务未被中止时,实现必须尝试提交该不活跃事务。
对
commit()的显式调用将发起一次提交,而不必等待脚本处理请求结果。提交时,事务状态被设置为正在提交。实现必须原子地写入由针对该事务的请求所做的对数据库的任何更改。也就是说,要么写入所有更改;如果发生错误(例如磁盘写入错误),实现不得将任何更改写入数据库,并且将遵循中止事务的步骤。
实现必须允许在事务活跃时,针对该事务发起请求。即使事务尚未启动,也是如此。在事务启动之前,实现不得执行这些请求;然而,实现必须跟踪这些请求及其顺序。
事务从其被创建直到其状态被设置为完成期间,被称为存活 (live)。
要清理 Indexed Database 事务,请运行以下步骤。如果清理了任何事务,它们将返回 true,否则返回 false。
注意:这些步骤由 [HTML] 调用。它们确保通过脚本调用 transaction() 创建的事务在调用该脚本的任务完成后被停用。对于每个事务,这些步骤最多运行一次。
类型为 complete 的事件会在已成功提交的事务上触发。
2.7.2. 事务调度
实现可能会施加额外的约束。例如,实现不需要并行启动非重叠的读/写事务,或者可能对启动的事务数量施加限制。
-
只要只读事务处于存活状态,实现通过使用该事务创建的请求返回的数据就保持不变。也就是说,两次读取同一数据块的请求产生相同的结果——无论是找到数据且结果为该数据的情况,还是未找到数据且表明缺少数据的情况。
-
读/写事务仅受使用该事务本身所做的对对象存储的更改影响。实现确保其他事务不会修改读/写事务的作用域内对象存储的内容。实现还确保如果读/写事务成功完成,则使用该事务写入对象存储的更改可以提交到数据库而不会发生合并冲突。
-
如果多个读/写事务试图访问同一个对象存储(即如果它们有重叠作用域),则最先创建的事务是首先获得对象存储访问权限的事务,并且它是唯一有权访问对象存储的事务,直到该事务完成。
-
任何在读/写事务之后创建的事务都会看到该读/写事务写入的更改。例如,如果创建了读/写事务 A,稍后创建了另一个事务 B,并且这两个事务有重叠作用域,那么事务 B 可以看到对该重叠作用域一部分的任何对象存储所做的任何更改。这也意味着在事务 A 完成之前,事务 B 无法访问该重叠作用域中的任何对象存储。
2.7.3. 升级事务
升级事务是模式为“versionchange”的事务。
在打开与数据库的连接后,如果指定了大于当前版本的版本,则在运行升级数据库的步骤时,会自动创建升级事务。此事务将在 upgradeneeded 事件处理程序内处于活跃状态。
升级事务是排他的。打开数据库连接的步骤确保当升级事务处于存活状态时,只有一个到数据库的连接是打开的。upgradeneeded 事件不会被触发,因此升级事务不会启动,直到所有其他到相同数据库的连接都已关闭。这确保了所有先前的事务都已完成。
只要升级事务处于存活状态,尝试打开更多到相同数据库的连接就会被延迟,并且任何通过调用 transaction() 试图使用同一连接来启动额外事务的操作都会抛出异常。这确保了没有其他事务并发存活,同时也确保了只要升级事务处于存活状态,就不会有新的事务针对同一数据库排队。
2.8. 请求
对数据库的每个异步操作都是使用请求完成的。每个请求代表一个操作。
请求拥有一个已处理标志 (processed flag),初始为 false。当与请求关联的操作被执行时,此标志被设置为 true。
请求拥有一个完成标志 (done flag),初始为 false。当与请求关联的操作结果可用时,此标志被设置为 true。
请求拥有一个源 (source)对象。
请求拥有一个结果和一个错误,在完成标志为 true 之前,两者均不可访问。
请求拥有一个事务,初始为 null。当使用异步执行请求的步骤将请求发起到事务中时,此项将被设置。
当发出一个请求时,返回一个新的请求,其完成标志设置为 false。如果请求成功完成,其完成标志被设置为 true,其结果被设置为请求的结果,并且类型为 success 的事件会在该请求上触发。
如果执行操作时发生错误,则请求的完成标志被设置为 true,请求的错误被设置为该错误,并且类型为 error 的事件会在该请求上触发。
注意:请求通常不会被重用,但也有例外。当游标进行迭代时,迭代的成功与否是在用于打开游标的同一个请求对象上报告的。当升级事务是必需时,同一个打开请求既用于 upgradeneeded 事件,也用于打开操作本身的最终结果。在某些情况下,请求的完成标志会被设置为 false,然后再次设置为 true,结果可能会改变,或者可能会设置错误。
2.8.1. 打开请求
打开请求是一种特殊的请求类型,用于打开连接或删除数据库。除了 success 和 error 事件外,blocked 和 upgradeneeded 事件可能会在打开请求上触发,以指示进度。
打开请求的事务为 null,除非已经触发了 upgradeneeded 事件。
2.8.2. 连接队列
打开请求在连接队列中处理。该队列包含所有与存储键和名称关联的打开请求。添加到连接队列的请求按顺序处理,每个请求必须运行至完成,下一个请求才能被处理。一个打开请求可能会被其他连接阻塞,需要这些连接关闭,请求才能完成并允许处理后续请求。
注意:连接队列不是与事件循环关联的任务队列,因为请求是在任何特定浏览上下文之外处理的。向已完成的打开请求交付事件仍会经过与发出请求上下文的事件循环关联的任务队列。
2.9. 键范围
可以使用键或键范围从对象存储和索引中检索记录。键范围是用于键的某种数据类型上的连续区间。
如果满足以下两个条件,则key 处于键范围 range 中:
无界键范围是键范围,其下界和上界均等于 null。所有键都处于无界键范围中。
要将值转换为键范围(具有 value 和可选的 null 不允许标志),请运行以下步骤:
潜在有效键范围是一个 ECMAScript 值,其类型可转换为键范围。特定值是否会成功转换为键范围(即对其执行将值转换为键范围操作是否抛出异常)并不相关。
注意:例如,已分离的 BufferSource 是一个潜在有效键范围,当与将值转换为键范围一起使用时会抛出异常。
要确定一个值何时是潜在有效键范围(具有 ECMAScript value),请运行以下步骤:
getAll() 和 getAllKeys() 方法使用是潜在有效键范围来处理它们的第一个参数。如果该参数是潜在有效键范围,getAll() 和 getAllKeys() 使用该参数运行将值转换为键范围。否则,IDBGetAllOptions 将用于第一个参数。getAll() 和 getAllKeys() 对第一个参数为 Date、Array 或 ArrayBuffer 的情况会抛出异常,当它们与将值转换为键一起使用时,会返回 “invalid value”。例如,使用 NaN Date 作为第一个参数运行 getAll() 会抛出异常,而不是成功使用具有默认值的 IDBGetAllOptions 字典。
2.10. 游标
游标用于在索引或对象存储中的一系列记录上以特定方向进行迭代。
游标有一个源句柄 (source handle),即打开游标的索引句柄或对象存储句柄。
游标有一个源 (source),即来自游标源句柄的索引或对象存储。游标的源指示游标正在迭代其记录的索引或对象存储是哪一个。如果游标的源句柄是索引句柄,则游标的源是该索引句柄关联的索引。否则,游标的源是该对象存储句柄关联的对象存储。
游标有一个方向,它决定了迭代时是按单调递增还是递减顺序移动记录键,以及在迭代索引时是否跳过重复值。游标的方向还决定了游标的初始位置是在其源的开头还是末尾。游标的方向是以下之一:
- "
next" - "
nextunique" -
此方向导致游标在源的开头打开。迭代时,游标不应产生具有相同键的记录,但除此之外,应以按键单调递增的顺序产生所有记录。对于每个具有重复值的键,仅产生第一条记录。当源是对象存储或索引且其唯一标志设置为 true 时,此方向的行为与 "
next" 完全相同。 - "
prev" - "
prevunique" -
此方向导致游标在源的末尾打开。迭代时,游标不应产生具有相同键的记录,但除此之外,应以按键单调递减的顺序产生所有记录。对于每个具有重复值的键,仅产生第一条记录。当源是对象存储或索引且其唯一标志设置为 true 时,此方向的行为与 "
prev" 完全相同。
游标在其范围内的位置。游标正在迭代的记录列表可能在游标完整范围迭代完成之前发生变化。为了处理这一点,游标不将它们的位置作为索引来维护,而是作为先前返回记录的键来维护。对于前向迭代游标,下一次游标被要求迭代到下一条记录时,它会返回大于先前返回的键的最低键的记录。对于反向迭代游标,情况相反,它返回小于先前返回的键的最高键的记录。
对于迭代索引的游标,情况稍微复杂一些,因为多条记录可以具有相同的键,因此也按值排序。迭代索引时,游标还有一个对象存储位置,它指示索引中先前找到的记录的值。在查找下一个适当的记录时,位置和对象存储位置都会被使用。
游标有一个got value 标志。当此标志为 false 时,游标要么正在加载下一个值,要么已达到其范围的末尾。当它为 true 时,表示游标当前持有值,并且已准备好迭代到下一个值。
如果游标的源是对象存储,则游标的有效对象存储是该对象存储,而游标的有效键是游标的位置。如果游标的源是索引,则游标的有效对象存储是该索引的引用的对象存储,而有效键是游标的对象存储位置。
2.11. 键生成器
当创建对象存储时,可以指定使用键生成器。如果未另外指定,键生成器将用于为插入到对象存储中的记录生成键。
键生成器拥有一个当前编号。该当前编号始终是一个正整数,且小于或等于 253 (9007199254740992) + 1。键生成器的当前编号的初始值为 1,在关联的对象存储创建时设置。当前编号随着键的生成而递增,并且可以通过使用显式键更新为特定值。
注意:每个使用键生成器的对象存储都使用单独的生成器。也就是说,与一个对象存储的交互绝不会影响任何其他对象存储的键生成器。
修改键生成器的当前编号被视为数据库操作的一部分。这意味着如果操作失败且操作被撤销,当前编号将恢复为操作开始之前的值。这既适用于由于生成键时当前编号增加 1 而发生的修改,也适用于由于在存储记录的调用中指定了键值而存储记录而发生的修改。
同样,如果事务被中止,事务作用域中每个对象存储的键生成器的当前编号将恢复为事务开始之前的值。
键生成器的当前编号绝不会减少,除非是数据库操作被撤销的结果。从对象存储中删除记录绝不会影响对象存储的键生成器。即使清除对象存储中的所有记录(例如使用 clear() 方法),也不会影响对象存储键生成器的当前编号。
要为对象存储 store 生成键,请运行以下步骤:
当存储记录且在存储记录的调用中指定了键时,关联的键生成器可能会被更新。
要可能更新具有 key 的对象存储 store 的键生成器,请运行以下步骤:
只有number类型的指定键会影响键生成器的当前编号。date、array(无论它们包含的其他键是什么)、binary 或 string(无论它们是否可以被解析为数字)类型的键不会对键生成器的当前编号产生影响。类型为 number 且值小于 1 的键不会影响当前编号,因为它们始终低于当前编号。
当键生成器的当前编号达到 253 (9007199254740992) 以上时,任何后续使用该键生成器生成新键的尝试都将导致 "ConstraintError" DOMException。仍可以通过指定显式键将记录插入到对象存储中,但是对于此类记录,再次使用键生成器的唯一方法是删除该对象存储并创建一个新的。
Number。例如,在 ECMAScript 中 9007199254740992 + 1 === 9007199254740992。只要正常使用键生成器,此限制就不会成为问题。如果您每秒生成 1000 个新键,日夜不停,您在超过 285000 年内都不会遇到此限制。
其实际结果是,为对象存储生成的第一个键始终是 1(除非首先插入了更高的数字键),并且为对象存储生成的键始终是大于存储中最高数字键的正整数。对于同一对象存储,同一键永远不会生成两次,除非事务被回滚。
每个对象存储都有自己的键生成器
store1= db. createObjectStore( "store1" , { autoIncrement: true }); store1. put( "a" ); // Will get key 1 store2= db. createObjectStore( "store2" , { autoIncrement: true }); store2. put( "a" ); // Will get key 1 store1. put( "b" ); // Will get key 2 store2. put( "b" ); // Will get key 2
如果由于约束冲突或 IO 错误导致插入失败,键生成器不会更新。
transaction. onerror= function ( e) { e. preventDefault() }; store= db. createObjectStore( "store1" , { autoIncrement: true }); index= store. createIndex( "index1" , "ix" , { unique: true }); store. put({ ix: "a" }); // Will get key 1 store. put({ ix: "a" }); // Will fail store. put({ ix: "b" }); // Will get key 2
从对象存储中删除项绝不会影响键生成器。包括调用 clear() 时。
store= db. createObjectStore( "store1" , { autoIncrement: true }); store. put( "a" ); // Will get key 1 store. delete ( 1 ); store. put( "b" ); // Will get key 2 store. clear(); store. put( "c" ); // Will get key 3 store. delete ( IDBKeyRange. lowerBound( 0 )); store. put( "d" ); // Will get key 4
插入带有显式键的项仅当键为数字且高于最后生成的键时,才会影响键生成器。
store= db. createObjectStore( "store1" , { autoIncrement: true }); store. put( "a" ); // Will get key 1 store. put( "b" , 3 ); // Will use key 3 store. put( "c" ); // Will get key 4 store. put( "d" , - 10 ); // Will use key -10 store. put( "e" ); // Will get key 5 store. put( "f" , 6.00001 ); // Will use key 6.0001 store. put( "g" ); // Will get key 7 store. put( "f" , 8.9999 ); // Will use key 8.9999 store. put( "g" ); // Will get key 9 store. put( "h" , "foo" ); // Will use key "foo" store. put( "i" ); // Will get key 10 store. put( "j" , [ 1000 ]); // Will use key [1000] store. put( "k" ); // Will get key 11 // All of these would behave the same if the objectStore used a // keyPath and the explicit key was passed inline in the object
中止事务会回滚事务期间发生的任何对键生成器的增加。这是为了使所有回滚保持一致,因为因崩溃而发生的回滚永远没有机会提交增加的键生成器值。
db. createObjectStore( "store" , { autoIncrement: true }); trans1= db. transaction([ "store" ], "readwrite" ); store_t1= trans1. objectStore( "store" ); store_t1. put( "a" ); // Will get key 1 store_t1. put( "b" ); // Will get key 2 trans1. abort(); trans2= db. transaction([ "store" ], "readwrite" ); store_t2= trans2. objectStore( "store" ); store_t2. put( "c" ); // Will get key 1 store_t2. put( "d" ); // Will get key 2
以下示例说明了在使用行内键和键生成器将对象保存到对象存储时,不同行为的各种情况。
如果以下条件为真:
那么由键生成器提供的值将被用于填充键值。在下面的示例中,对象存储的键路径为 "foo.bar"。实际对象没有 bar 属性的值,即 { foo: {} }。当对象保存到对象存储时,bar 属性被分配值 1,因为这是由键生成器生成的下一个键。
const store= db. createObjectStore( "store" , { keyPath: "foo.bar" , autoIncrement: true }); store. put({ foo: {} }). onsuccess= function ( e) { const key= e. target. result; console. assert( key=== 1 ); };
如果以下条件为真:
那么与键路径属性关联的值将被使用。自动生成的键不会被使用。在下面的示例中,对象存储的键路径为 "foo.bar"。实际对象对于 bar 属性有 10 的值,即 { foo: { bar: 10} }。当对象保存到对象存储时,bar 属性保持其值 10,因为那是键值。
const store= db. createObjectStore( "store" , { keyPath: "foo.bar" , autoIncrement: true }); store. put({ foo: { bar: 10 } }). onsuccess= function ( e) { const key= e. target. result; console. assert( key=== 10 ); };
以下示例说明了通过键路径定义了指定行内键,但没有与其匹配的属性的场景。然后,由键生成器提供的值将被用于填充键值,系统负责创建满足层次结构链上属性依赖关系所需的所有属性。在下面的示例中,对象存储的键路径为 "foo.bar.baz"。实际对象没有 foo 属性的值,即 { zip: {} }。当对象保存到对象存储时,会创建 foo、bar 和 baz 属性,每一个都是另一个的子级,直到可以为 foo.bar.baz 分配值。foo.bar.baz 的值是对象存储生成的下一个键。
const store= db. createObjectStore( "store" , { keyPath: "foo.bar.baz" , autoIncrement: true }); store. put({ zip: {} }). onsuccess= function ( e) { const key= e. target. result; console. assert( key=== 1 ); store. get( key). onsuccess= function ( e) { const value= e. target. result; // value will be: { zip: {}, foo: { bar: { baz: 1 } } } console. assert( value. foo. bar. baz=== 1 ); }; };
尝试在基元值上存储属性将会失败并抛出错误。在下面的第一个示例中,对象存储的键路径为 "foo"。实际对象是一个值为 4 的基元。尝试在该基元值上定义属性会失败。
const store= db. createObjectStore( "store" , { keyPath: "foo" , autoIncrement: true }); // The key generation will attempt to create and store the key path // property on this primitive. store. put( 4 ); // will throw DataError
2.12. 记录快照
注意:对于索引记录,快照的值是该记录的引用值的副本。对于对象存储记录,快照的值是该记录的值。
注意:对于索引记录,快照的主键是该记录的值,即该记录在索引的引用对象存储中的键。对于对象存储记录,快照的主键和键是同一个键,即该记录的键。
3. 异常
本文档中使用的每个异常都是一个 DOMException 或派生自 DOMException 的接口,如 [WEBIDL] 中所定义。
下表列出了本文档中使用的 DOMException 名称及其用法说明。
| 类型 | 描述 |
|---|---|
AbortError
| 请求被中止。 |
ConstraintError
| 事务中的修改操作失败,因为未满足约束条件。 |
DataCloneError
| 正在存储的数据无法通过内部结构化克隆算法进行克隆。 |
DataError
| 提供给操作的数据不符合要求。 |
InvalidAccessError
| 对对象执行了无效操作。 |
InvalidStateError
| 在不允许调用的对象上调用了某个操作,或者在不允许的时间调用了该操作,或者请求是在已被删除或移除的源对象上发出的。 |
NotFoundError
| 操作失败,因为找不到请求的数据库对象。 |
NotReadableError
| 操作失败,因为无法读取包含请求数据的底层存储。 |
SyntaxError
| keyPath 参数包含无效的键路径。 |
ReadOnlyError
| 在只读事务中尝试了修改操作。 |
TransactionInactiveError
| 向当前处于非活动状态或已完成的事务提交了请求。 |
UnknownError
| 操作因与数据库本身无关或未被其他错误涵盖的临时原因而失败。 |
VersionError
| 尝试使用低于现有版本的版本打开数据库。 |
除了上述 DOMException 名称外,如果操作因剩余存储空间不足,或已达到存储配额且用户拒绝为数据库提供更多空间而失败,则应使用 QuotaExceededError 异常类型。
注意: 鉴于多个 Indexed DB 操作可能会抛出相同类型的错误,甚至单个操作也可能因多种原因抛出相同类型的错误,建议实现提供更具体的错误消息,以便开发者识别错误原因。
4. API
API 方法返回时不会阻塞调用线程。所有异步操作都会立即返回一个 IDBRequest 实例。此对象最初不包含有关操作结果的任何信息。一旦信息可用,就会在该请求上触发一个事件,并且该信息可以通过 IDBRequest 实例的属性获取。
这些任务的 任务源 为 数据库访问任务源。
4.1. IDBRequest 接口
IDBRequest 接口提供了使用 事件处理程序 IDL 属性 [HTML] 访问对 数据库 和 数据库 对象发出的异步请求结果的方法。
每个用于发出异步请求的方法都会返回一个 IDBRequest 对象,该对象通过事件与请求应用程序通信。这种设计意味着在任何给定时间,任何 数据库 上都可以有任意数量的活跃请求。
在以下示例中,我们异步打开了一个 数据库。注册了各种事件处理程序以应对各种情况。
const request= indexedDB. open( 'AddressBook' , 15 ); request. onsuccess= function ( evt) {...}; request. onerror= function ( evt) {...};
[Exposed =(Window ,Worker )]interface :IDBRequest EventTarget {readonly attribute any result ;readonly attribute DOMException ?error ;readonly attribute (IDBObjectStore or IDBIndex or IDBCursor )?source ;readonly attribute IDBTransaction ?transaction ;readonly attribute IDBRequestReadyState readyState ; // Event handlers:attribute EventHandler onsuccess ;attribute EventHandler onerror ; };enum {IDBRequestReadyState ,"pending" };"done"
- request .
result -
当请求完成时,返回 结果,如果请求失败,则返回
undefined。如果请求仍处于挂起状态,则抛出 "InvalidStateError"DOMException。 - request .
error -
当请求完成时,返回 错误(一个
DOMException),如果请求成功,则返回 null。如果请求仍处于挂起状态,则抛出 "InvalidStateError"DOMException。 - request .
source -
返回发出请求的
IDBObjectStore、IDBIndex或IDBCursor,如果是 打开请求,则返回 null。 - request .
transaction -
返回发出请求所在的
IDBTransaction。如果是 打开请求,则在 活跃 时返回 升级事务,否则返回 null。 - request .
readyState
result 获取器步骤为-
如果 此对象 的 完成标志 为 false,则 抛出 "
InvalidStateError"DOMException。
error 获取器步骤为-
如果 此对象 的 完成标志 为 false,则 抛出 "
InvalidStateError"DOMException。
source 获取器步骤为返回 此对象 的 来源,如果未设置 来源,则返回 null。
transaction 获取器步骤为返回 此对象 的 事务。
注意: transaction 获取器对于某些请求(例如从 open() 返回的 请求)可能返回 null。
readyState 获取器步骤为:如果 此对象 的 完成标志 为 false,则返回 "pending",否则返回 "done"。
onsuccess 属性是一个 事件处理程序 IDL 属性,其 事件处理程序事件类型 为 success。
onerror 属性是一个 事件处理程序 IDL 属性,其 事件处理程序事件类型 为 error 事件。
IDBDatabase 上返回 打开请求 的方法使用扩展接口,以允许监听 blocked 和 upgradeneeded 事件。
[Exposed =(Window ,Worker )]interface :IDBOpenDBRequest IDBRequest { // Event handlers:attribute EventHandler onblocked ;attribute EventHandler onupgradeneeded ; };
onblocked 属性是一个 事件处理程序 IDL 属性,其 事件处理程序事件类型 为 blocked。
onupgradeneeded 属性是一个 事件处理程序 IDL 属性,其 事件处理程序事件类型 为 upgradeneeded。
4.2. 事件接口
本规范使用以下自定义接口触发事件
[Exposed =(Window ,Worker )]interface :IDBVersionChangeEvent Event {(constructor DOMString ,type optional IDBVersionChangeEventInit = {});eventInitDict readonly attribute unsigned long long oldVersion ;readonly attribute unsigned long long ?newVersion ; };dictionary :IDBVersionChangeEventInit EventInit {unsigned long long = 0;oldVersion unsigned long long ?=newVersion null ; };
oldVersion 获取器步骤为返回其初始化时设置的值。它表示数据库的上一个版本。
newVersion 获取器步骤为返回其初始化时设置的值。它表示数据库的新版本,如果数据库正在被删除,则返回 null。参见 升级数据库 的步骤。
事件的构造按照 DOM § 2.5 构造事件 定义。
要 触发一个版本更改事件,命名为 e,在 target 上,给定 oldVersion 和 newVersion,运行这些步骤
-
令 event 为使用
IDBVersionChangeEvent创建事件 的结果。 -
将 event 的
type属性设置为 e。 -
将 event 的
bubbles和cancelable属性设置为 false。 -
将 event 的
oldVersion属性设置为 oldVersion。 -
将 event 的
newVersion属性设置为 newVersion。 -
令 legacyOutputDidListenersThrowFlag 为 false。
-
分派 event 到 target,使用 legacyOutputDidListenersThrowFlag。
-
返回 legacyOutputDidListenersThrowFlag。
注意: 此算法的返回值并不总是被使用。
4.3. IDBFactory 接口
数据库 对象通过 IDBFactory 接口上的方法访问。在支持 Indexed DB 操作的环境的全局作用域中,存在一个实现此接口的单一对象。
partial interface mixin WindowOrWorkerGlobalScope { [SameObject ]readonly attribute IDBFactory indexedDB ; };
indexedDB 属性为应用程序提供了访问索引数据库功能的机制。
[Exposed =(Window ,Worker )]interface { [IDBFactory NewObject ]IDBOpenDBRequest open (DOMString ,name optional [EnforceRange ]unsigned long long ); [version NewObject ]IDBOpenDBRequest deleteDatabase (DOMString );name Promise <sequence <IDBDatabaseInfo >>databases ();short cmp (any ,first any ); };second dictionary {IDBDatabaseInfo DOMString ;name unsigned long long ; };version
- request = indexedDB .
open(name) -
尝试打开一个到名为 name 的 数据库 的 连接,使用当前版本,如果不存在则使用 1。如果请求成功,request 的
result将为该 连接。 - request = indexedDB .
open(name, version) -
尝试打开一个到名为 name 的 数据库 的 连接,使用指定的 version。如果数据库已经存在且版本较低,且存在响应
versionchange事件未关闭的打开 连接,则请求将被阻塞,直到它们全部关闭,然后进行升级。如果数据库已经存在且版本较高,则请求将失败。如果请求成功,request 的result将为该 连接。 - request = indexedDB .
deleteDatabase(name) -
尝试删除名为 name 的 数据库。如果数据库已经存在且存在响应
versionchange事件未关闭的打开 连接,则请求将被阻塞,直到它们全部关闭。如果请求成功,request 的result将为 null。 - result = await indexedDB .
databases() -
返回一个 promise,该 promise 解析为一个对象列表,提供 存储键 内数据库名称和版本的快照。
此 API 旨在供 Web 应用程序自省数据库的使用情况,例如清理网站代码的旧版本。请注意,结果是一个快照;对于此上下文或其他上下文创建、升级或删除数据库的请求,不保证数据的收集顺序或响应的传递顺序。
open(name, version) 方法步骤为
-
令 storageKey 为运行 获取存储键 给定 environment 的结果。如果返回失败,则 抛出 "
SecurityError"DOMException并中止这些步骤。 -
令 request 为一个新的 打开请求。
-
以并行方式运行这些步骤
-
令 result 为 打开数据库连接 的结果,包含 storageKey、name、如果给定则包含 version 否则为 undefined,以及 request。
-
将 request 的 已处理标志 设置为 true。
-
排队一个数据库任务 以运行以下步骤
-
如果 result 是错误,则
-
否则
注意: 如果上述步骤导致执行了 升级事务,则这些步骤将在该事务完成后运行。这确保了在即将发生另一次版本升级的情况下,success 事件会首先在连接上触发,以便脚本有机会为
versionchange事件注册监听器。为什么不使用 触发 success 事件 或 触发 error 事件 的步骤?
该请求(此时)没有关联的事务,因此那些在分派前激活关联事务并在分派后停用事务的步骤不适用。
-
-
-
为 request 返回一个新的
IDBOpenDBRequest对象。
deleteDatabase(name) 方法步骤为
-
令 storageKey 为运行 获取存储键 给定 environment 的结果。如果返回失败,则 抛出 "
SecurityError"DOMException并中止这些步骤。 -
令 request 为一个新的 打开请求。
-
并行运行这些步骤 并行运行
-
令 result 为 删除数据库 的结果,包含 storageKey、name 和 request。
-
将 request 的 已处理标志 设置为 true。
-
排队一个数据库任务 以运行以下步骤
-
如果 result 是错误,则将 request 的 错误 设置为 result,将 request 的 完成标志 设置为 true,并 触发一个事件,命名为
error,在 request 上,其bubbles和cancelable属性初始化为 true。 -
否则,将 request 的 结果 设置为 undefined,将 request 的 完成标志 设置为 true,并 触发一个版本更改事件,命名为
success,在 request 上,使用 result 和 null。为什么不使用 触发 success 事件 或 触发 error 事件 的步骤?
请求没有关联的事务,因此那些在分派前激活关联事务并在分派后停用事务的步骤不适用。此外,这里的
success事件是一个IDBVersionChangeEvent,其中包含了oldVersion和newVersion的详细信息。
-
-
-
为 request 返回一个新的
IDBOpenDBRequest对象。
databases() 方法步骤为
-
令 storageKey 为运行 获取存储键 给定 environment 的结果。如果返回失败,则返回 一个被拒绝的 promise,包含一个 "
SecurityError"DOMException。 -
令 p 为一个新的 promise。
-
并行运行这些步骤 并行运行
-
令 databases 为 storageKey 中的 数据库 的 集合。如果因任何原因无法确定,则 排队一个数据库任务 以 拒绝 p,并返回适当的错误(例如 "
UnknownError"DOMException)并终止这些步骤。 -
令 result 为一个新的 列表。
-
对于每个 databases 中的 db
-
-
返回 p。
databases() 方法是本版本新增的。它在 Chrome 71、Edge 79、Firefox 126 和 Safari 14 中得到支持。 🚧- result = indexedDB .
cmp(key1, key2) -
比较两个值作为 键。如果 key1 先于 key2,则返回 -1;如果 key2 先于 key1,则返回 1;如果键相等,则返回 0。
如果任何一个输入不是有效的 键,则抛出 "
DataError"DOMException。
cmp(first, second) 方法步骤为
-
令 a 为 将值转换为键 的结果,使用 first。重新抛出任何异常。
-
如果 a 为 "invalid value" 或 "invalid type",则 抛出 "
DataError"DOMException。 -
令 b 为 将值转换为键 的结果,使用 second。重新抛出任何异常。
-
如果 b 为 "invalid value" 或 "invalid type",则 抛出 "
DataError"DOMException。 -
返回 比较两个键 的结果,使用 a 和 b。
4.4. IDBDatabase 接口
IDBDatabase 接口代表一个到 数据库 的 连接。
如果其关联的 连接 的 关闭挂起标志 为 false,且注册了一个或多个类型为 abort、error 或 versionchange 的事件监听器,则 IDBDatabase 对象不得被垃圾回收。如果一个 IDBDatabase 对象被垃圾回收,则必须 关闭 关联的 连接。
[Exposed =(Window ,Worker )]interface :IDBDatabase EventTarget {readonly attribute DOMString name ;readonly attribute unsigned long long version ;readonly attribute DOMStringList objectStoreNames ; [NewObject ]IDBTransaction transaction ((DOMString or sequence <DOMString >),storeNames optional IDBTransactionMode = "readonly",mode optional IDBTransactionOptions = {});options undefined close (); [NewObject ]IDBObjectStore createObjectStore (DOMString ,name optional IDBObjectStoreParameters = {});options undefined deleteObjectStore (DOMString ); // Event handlers:name attribute EventHandler onabort ;attribute EventHandler onclose ;attribute EventHandler onerror ;attribute EventHandler onversionchange ; };enum {IDBTransactionDurability ,"default" ,"strict" };"relaxed" dictionary {IDBTransactionOptions IDBTransactionDurability = "default"; };durability dictionary { (IDBObjectStoreParameters DOMString or sequence <DOMString >)?=keyPath null ;boolean =autoIncrement false ; };
name 获取器步骤为返回 此对象 关联的 数据库 的 名称。
注意: 即使 此对象 的 关闭挂起标志 为 true,name 属性也会返回此名称。换句话说,此属性的值在 IDBDatabase 实例的生命周期内保持不变。
这与 数据库 的 版本 相同吗?
只要 连接 处于打开状态,它就与已连接的 数据库 的 版本 相同。但一旦 连接 关闭,此属性将不会反映稍后通过 升级事务 所做的更改。- connection .
objectStoreNames -
返回数据库中 对象存储 的名称列表。
- store = connection .
createObjectStore(name [, options]) -
创建一个具有给定 name 和 options 的新 对象存储,并返回一个新的
IDBObjectStore。如果未在 升级事务 中调用,则抛出 "
InvalidStateError"DOMException。 - connection .
deleteObjectStore(name) -
删除名为 name 的 对象存储。
如果未在 升级事务 中调用,则抛出 "
InvalidStateError"DOMException。
objectStoreNames 获取器步骤为
这与 数据库 的 对象存储 名称 相同吗?
只要 连接 处于打开状态,它就与已连接的 数据库 的 对象存储 名称 相同。但一旦 连接 关闭,此属性将不会反映稍后通过 升级事务 所做的更改。createObjectStore(name, options) 方法步骤为
-
令 transaction 为 database 的 升级事务(如果不是 null),否则 抛出 "
InvalidStateError"DOMException。 -
如果 transaction 的 状态 不为 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
令 keyPath 为 options 的
keyPath成员(如果它不是 undefined 或 null),否则为 null。 -
如果 keyPath 不为 null 且不是 有效的键路径,则 抛出 "
SyntaxError"DOMException。 -
如果 database 中已存在 命名为 name 的 对象存储,则 抛出 "
ConstraintError"DOMException。 -
令 autoIncrement 为 options 的
autoIncrement成员。 -
如果 autoIncrement 为 true 且 keyPath 为空字符串或任何序列(空或非空),则 抛出 "
InvalidAccessError"DOMException。 -
令 store 为 database 中的一个新的 对象存储。将创建的 对象存储 的 名称 设置为 name。如果 autoIncrement 为 true,则创建的 对象存储 使用 键生成器。如果 keyPath 不为 null,则将创建的 对象存储 的 键路径 设置为 keyPath。
-
返回一个与 store 和 transaction 关联的新的 对象存储句柄。
此方法在 已连接 的 数据库 中创建并返回具有给定名称的新 对象存储。请注意,此方法只能在 升级事务 中调用。
此方法同步修改在其上调用的 IDBDatabase 实例上的 objectStoreNames 属性。
在某些实现中,在 createObjectStore() 方法返回后,在排队创建一个 对象存储 的任务后,实现可能会遇到问题。例如,在元数据被异步插入到数据库中,或者实现可能因配额原因需要询问用户权限的实现中。此类实现仍必须创建并返回一个 IDBObjectStore 对象,一旦实现确定创建 对象存储 已失败,就必须使用 中止事务 的步骤使用适当的错误来中止事务。例如,如果创建 对象存储 因配额原因失败,则必须将 QuotaExceededError 用作错误。
deleteObjectStore(name) 方法步骤为
-
令 transaction 为 database 的 升级事务(如果不是 null),否则 抛出 "
InvalidStateError"DOMException。 -
如果 transaction 的 状态 不为 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
令 store 为 database 中 命名为 name 的 对象存储,如果不存在,则 抛出 "
NotFoundError"DOMException。 -
销毁 store。
此方法在 已连接 的 数据库 中销毁具有给定名称的 对象存储。请注意,此方法只能在 升级事务 中调用。
此方法同步修改在其上调用的 IDBDatabase 实例上的 objectStoreNames 属性。
- transaction = connection .
transaction(scope [, mode [, options ] ]) -
返回一个新的 事务,具有给定的 scope(可以是单个 对象存储 名称 或名称数组)、mode ("
readonly" 或 "readwrite") 以及包括durability("default", "strict" 或 "relaxed") 的附加 options。默认的 mode 为 "
readonly",默认的durability为 "default"。 - connection .
close()
transaction(storeNames, mode, options) 方法步骤为
-
如果一个 活跃 的 升级事务 与此 连接 关联,则 抛出 "
InvalidStateError"DOMException。 -
如果 此对象 的 关闭挂起标志 为 true,则 抛出 "
InvalidStateError"DOMException。 -
令 scope 为 storeNames 中的唯一字符串集合(如果是序列),否则为包含一个等于 storeNames 的字符串的集合。
-
如果 scope 中的任何字符串不是 已连接 数据库 中 对象存储 的名称,则 抛出 "
NotFoundError"DOMException。 -
如果 scope 为空,则 抛出 "
InvalidAccessError"DOMException。 -
令 transaction 为一个新的 创建的 事务,具有此 连接、mode、options 的
durability成员,以及 scope 中命名的 对象存储 集合。 -
返回一个代表 transaction 的
IDBTransaction对象。
durability 选项是本版本新增的。它在 Chrome 82、Edge 82、Firefox 126 和 Safari 15 中得到支持。 🚧注意: 创建的 transaction 将遵循 生命周期 规则。
注意: 连接 不会真正 关闭,直到所有未完成的 事务 完成。后续调用 close() 将不会有任何效果。
onabort 属性是一个 事件处理程序 IDL 属性,其 事件处理程序事件类型 为 abort。
onclose 属性是一个 事件处理程序 IDL 属性,其 事件处理程序事件类型 为 close。
onerror 属性是一个 事件处理程序 IDL 属性,其 事件处理程序事件类型 为 error。
onversionchange 属性是一个 事件处理程序 IDL 属性,其 事件处理程序事件类型 为 versionchange。
4.5. IDBObjectStore 接口
IDBObjectStore 接口代表一个 对象存储句柄。
[Exposed =(Window ,Worker )]interface {IDBObjectStore attribute DOMString name ;readonly attribute any keyPath ;readonly attribute DOMStringList indexNames ; [SameObject ]readonly attribute IDBTransaction transaction ;readonly attribute boolean autoIncrement ; [NewObject ]IDBRequest put (any ,value optional any ); [key NewObject ]IDBRequest add (any ,value optional any ); [key NewObject ]IDBRequest delete (any ); [query NewObject ]IDBRequest clear (); [NewObject ]IDBRequest get (any ); [query NewObject ]IDBRequest getKey (any ); [query NewObject ]IDBRequest getAll (optional any ,queryOrOptions optional [EnforceRange ]unsigned long ); [count NewObject ]IDBRequest getAllKeys (optional any ,queryOrOptions optional [EnforceRange ]unsigned long ); [count NewObject ]IDBRequest getAllRecords (optional IDBGetAllOptions = {}); [options NewObject ]IDBRequest count (optional any ); [query NewObject ]IDBRequest openCursor (optional any ,query optional IDBCursorDirection = "next"); [direction NewObject ]IDBRequest openKeyCursor (optional any ,query optional IDBCursorDirection = "next");direction IDBIndex index (DOMString ); [name NewObject ]IDBIndex createIndex (DOMString , (name DOMString or sequence <DOMString >),keyPath optional IDBIndexParameters = {});options undefined deleteIndex (DOMString ); };name dictionary {IDBIndexParameters boolean =unique false ;boolean =multiEntry false ; };dictionary {IDBGetAllOptions any =query null ; [EnforceRange ]unsigned long ;count IDBCursorDirection = "next"; };direction
- store .
name -
返回存储的 名称。
- store .
name= newName -
将存储的 名称 更新为 newName。
如果未在 升级事务 中调用,则抛出 "
InvalidStateError"DOMException。 - store .
keyPath -
返回存储的 键路径,如果无则返回 null。
- store .
indexNames -
返回存储中索引的名称列表。
- store .
transaction -
返回关联的 事务。
- store .
autoIncrement -
如果存储具有 键生成器,则返回 true,否则返回 false。
name getter 的步骤是返回 this 的 name。
这是否与 object store 的 name 相同?
只要 transaction 未 finished,这就与关联的 object store 的 name 相同。但一旦 transaction finished,此属性将不会反映后续 upgrade transaction 所做的更改。name setter 的步骤是
-
令 name 为 the given value。
-
令 transaction 为 this 的 transaction。
-
令 store 为 this 的 object store。
-
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 不是 upgrade transaction,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 不是 active,则 throw 一个 "
TransactionInactiveError"DOMException。 -
如果 store 的 name 等于 name,则终止这些步骤。
-
如果 store 的 database 中已存在一个 object store named name,则 throw 一个 "
ConstraintError"DOMException。 -
将 store 的 name 设置为 name。
keyPath getter 的步骤是返回 this 的 object store 的 key path,如果不存在则返回 null。根据 [WEBIDL],key path 会被转换为 DOMString(如果是一个字符串)或 sequence<(如果是一个字符串列表)。DOMString>
注意: 返回的值不是创建 object store 时使用的同一实例。但是,如果此属性返回一个对象(特别是 Array),则每次检查时都会返回相同的对象实例。更改该对象的属性对 object store 没有影响。
indexNames getter 的步骤是-
返回 creating a sorted name list 使用 names 的结果(一个
DOMStringList)。
这是否与 object store 的 index names 列表相同?
只要 transaction 未 finished,这就与关联的 object store 的 index names 列表相同。但一旦 transaction finished,此属性将不会反映后续 upgrade transaction 所做的更改。transaction getter 的步骤是返回 this 的 transaction。
autoIncrement getter 的步骤是:如果 this 的 object store 拥有 key generator 则返回 true,否则返回 false。
ReadOnlyError" DOMException;如果在 transaction 未 active 时调用,则 throw 一个 "TransactionInactiveError" DOMException。- request = store .
put(value [, key]) - request = store .
add(value [, key]) -
使用给定的 value 和 key 在 store 中添加或更新 record。
如果 store 使用 in-line keys 且指定了 key,则会 throw 一个 "
DataError"DOMException。如果使用
put(),则会替换该 key 下的任何现有 record。如果使用add(),且该 key 下已存在 record,则 request 将失败,并将 request 的error设置为 "ConstraintError"DOMException。 - request = store .
delete(query) -
删除 store 中与给定的 key 或 query 中的 key range 匹配的 records。
如果成功,request 的
result将是undefined。 - request = store .
clear() -
删除 store 中的所有 records。
如果成功,request 的
result将是undefined。
put(value, key) 方法的步骤是返回运行 add or put 的结果,参数为 this、value、key 以及 no-overwrite flag false。
add(value, key) 方法的步骤是返回运行 add or put 的结果,参数为 this、value、key 以及 no-overwrite flag true。
要 add or put 使用 handle、value、key 和 no-overwrite flag,请运行以下步骤:
-
令 transaction 为 handle 的 transaction。
-
令 store 为 handle 的 object store。
-
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 不是 active,则 throw 一个 "
TransactionInactiveError"DOMException。 -
如果 transaction 是 read-only transaction,则 throw 一个 "
ReadOnlyError"DOMException。 -
如果 store 使用 in-line keys 且给定了 key,则 throw 一个 "
DataError"DOMException。 -
如果 store 使用 out-of-line keys 且没有 key generator,且未给定 key,则 throw 一个 "
DataError"DOMException。 -
如果给定了 key,则
-
令 r 为 converting a value to a key 使用 key 的结果。重新抛出任何异常。
-
如果 r 是“invalid value”或“invalid type”,则 throw 一个 "
DataError"DOMException。 -
令 key 为 r。
-
-
令 targetRealm 为用户代理定义的 Realm。
-
令 clone 为 value 在 targetRealm 中于 transaction 期间的 clone。重新抛出任何异常。
为什么要创建值的副本?
值在存储时会被序列化。在此将其视为副本允许规范中的其他算法将其视为 ECMAScript 值,但如果行为差异不可观察,实现可以对其进行优化。 -
如果 store 使用 in-line keys,则
-
令 kpk 为使用 clone 和 store 的 key path extracting a key from a value using a key path 的结果。重新抛出任何异常。
-
如果 kpk 无效,则 throw 一个 "
DataError"DOMException。 -
如果 kpk 不是失败(failure),令 key 为 kpk。
-
否则(kpk 为 failure)
-
如果 store 没有 key generator,则 throw 一个 "
DataError"DOMException。 -
如果使用 clone 和 store 的 key path 执行 check that a key could be injected into a value 返回 false,则 throw 一个 "
DataError"DOMException。
-
-
-
令 operation 为运行 store a record into an object store 的算法,参数为 store、clone、key 和 no-overwrite flag。
-
返回运行 asynchronously execute a request 的结果(一个
IDBRequest),参数为 handle 和 operation。
delete(query) 方法的步骤是
-
令 transaction 为 this 的 transaction。
-
令 store 为 this 的 object store。
-
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 不是 active,则 throw 一个 "
TransactionInactiveError"DOMException。 -
如果 transaction 是 read-only transaction,则 throw 一个 "
ReadOnlyError"DOMException。 -
令 range 为 converting a value to a key range 使用 query 和 true 的结果。重新抛出任何异常。
-
令 operation 为运行 delete records from an object store 的算法,参数为 store 和 range。
-
返回运行 asynchronously execute a request 的结果(一个
IDBRequest),参数为 this 和 operation。
注意: query 参数可以是标识要删除的 records 的 key 或 key range(一个 IDBKeyRange)。
注意: 与其他接受 key 或 key range 的方法不同,此方法不允许将 null 作为 key 传入。这是为了降低因小错误而清空整个 object store 的风险。
clear() 方法的步骤是
-
令 transaction 为 this 的 transaction。
-
令 store 为 this 的 object store。
-
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 不是 active,则 throw 一个 "
TransactionInactiveError"DOMException。 -
如果 transaction 是 read-only transaction,则 throw 一个 "
ReadOnlyError"DOMException。 -
令 operation 为运行 clear an object store 的算法,参数为 store。
-
返回运行 asynchronously execute a request 的结果(一个
IDBRequest),参数为 this 和 operation。
TransactionInactiveError" DOMException。- request = store .
get(query) -
检索与给定的 key 或 query 中的 key range 匹配的第一个 record 的 value。
如果成功,request 的
result将是该 value;如果不存在匹配的 record,则为undefined。 - request = store .
getKey(query) - request = store .
getAll(query [, count]) - request = store .
getAll({query, count, direction}) -
检索 query 中与给定的 key 或 key range 匹配的 records 的 values(如果指定,最多为 count 个)。将 direction 选项设置为 "
next" 以检索前 count 个值,或设置为 "prev" 以返回最后 count 个值。 - request = store .
getAllKeys(query [, count]) - request = store .
getAllKeys({query, count, direction}) -
检索 query 中与给定的 key 或 key range 匹配的 records 的 keys(如果指定,最多为 count 个)。将 direction 选项设置为 "
next" 以检索前 count 个 key,或设置为 "prev" 以返回最后 count 个 key。 - request = store .
getAllRecords({query, count, direction}) -
query 选项指定要匹配的 key 或 key range。count 选项限制匹配的记录数。将 direction 选项设置为 "
next" 以检索前 count 个记录,或设置为 "prev" 以返回最后 count 个记录。 - request = store .
count(query) -
检索与给定的 key 或 query 中的 key range 匹配的 records 的数量。
如果成功,request 的
result将是该计数。
get(query) 方法的步骤是
-
令 transaction 为 this 的 transaction。
-
令 store 为 this 的 object store。
-
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 不是 active,则 throw 一个 "
TransactionInactiveError"DOMException。 -
令 range 为 converting a value to a key range 使用 query 和 true 的结果。重新抛出任何异常。
-
令 operation 为运行 retrieve a value from an object store 的算法,参数为 the current Realm record、store 和 range。
-
返回运行 asynchronously execute a request 的结果(一个
IDBRequest),参数为 this 和 operation。
注意: query 参数可以是标识要检索的 record 值的 key 或 key range(一个 IDBKeyRange)。如果指定了范围,该方法会检索该范围内第一个存在的值。
注意: 如果具有给定 key 的记录不存在,则此方法产生的结果与记录存在但值为 undefined 时产生的结果相同。如果您需要区分这两种情况,可以使用具有相同 key 的 openCursor()。如果记录存在,这将返回一个值为 undefined 的游标;如果不存在此类记录,则不返回游标。
getKey(query) 方法的步骤是
-
令 transaction 为 this 的 transaction。
-
令 store 为 this 的 object store。
-
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 不是 active,则 throw 一个 "
TransactionInactiveError"DOMException。 -
令 range 为 converting a value to a key range 使用 query 和 true 的结果。重新抛出任何异常。
-
令 operation 为运行 retrieve a key from an object store 的算法,参数为 store 和 range。
-
返回运行 asynchronously execute a request 的结果(一个
IDBRequest),参数为 this 和 operation。
注意: query 参数可以是标识要检索的 record key 的 key 或 key range(一个 IDBKeyRange)。如果指定了范围,该方法会检索该范围内第一个存在的 key。
getAll(queryOrOptions, count) 方法的步骤是
-
返回 creating a request to retrieve multiple items 的结果,参数为 the current Realm record、this、"value"、queryOrOptions 以及(如果给定的话)count。重新抛出任何异常。
getAllKeys(queryOrOptions, count) 方法的步骤是
-
返回 creating a request to retrieve multiple items 的结果,参数为 the current Realm record、this、"key"、queryOrOptions 以及(如果给定的话)count。重新抛出任何异常。
getAllRecords(options) 方法的步骤是
-
返回 creating a request to retrieve multiple items 的结果,参数为 the current Realm record、this、"record" 和 options。重新抛出任何异常。
count(query) 方法的步骤是
-
令 transaction 为 this 的 transaction。
-
令 store 为 this 的 object store。
-
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 不是 active,则 throw 一个 "
TransactionInactiveError"DOMException。 -
令 range 为 converting a value to a key range 使用 query 的结果。重新抛出任何异常。
-
令 operation 为运行 count the records in a range 的算法,参数为 store 和 range。
-
返回运行 asynchronously execute a request 的结果(一个
IDBRequest),参数为 this 和 operation。
注意: query 参数可以是标识要计数的 records 的 key 或 key range(一个 IDBKeyRange)。如果为 null 或未给定,则使用 unbounded key range。
TransactionInactiveError" DOMException。- request = store .
openCursor([query [, direction = "next"]]) -
在一个 cursor 上开启一个游标,覆盖与 query 匹配的 records,并按 direction 排序。如果 query 为 null,则 store 中的所有 records 都会被匹配。
如果成功,request 的
result将是一个指向第一个匹配的 record 的IDBCursorWithValue;如果没有匹配的 records,则为 null。 - request = store .
openKeyCursor([query [, direction = "next"]]) -
开启一个 cursor,其 key only flag 设置为 true,覆盖与 query 匹配的 records,并按 direction 排序。如果 query 为 null,则 store 中的所有 records 都会被匹配。
如果成功,request 的
result将是一个指向第一个匹配的 record 的IDBCursor;如果没有匹配的 records,则为 null。
openCursor(query, direction) 方法的步骤是
-
令 transaction 为 this 的 transaction。
-
令 store 为 this 的 object store。
-
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 不是 active,则 throw 一个 "
TransactionInactiveError"DOMException。 -
令 range 为 converting a value to a key range 使用 query 的结果。重新抛出任何异常。
-
令 cursor 为一个新的 cursor,其 source handle 设置为 this,position 为 undefined,direction 设置为 direction,got value flag 设置为 false,key 和 value 为 undefined,range 设置为 range,且 key only flag 设置为 false。
-
令 operation 为运行 iterate a cursor 的算法,参数为 the current Realm record 和 cursor。
-
令 request 为运行 asynchronously execute a request 的结果,参数为 this 和 operation。
-
将 cursor 的 request 设置为 request。
-
返回 request。
注意: query 参数可以是标识用作 cursor 的 range 的 key 或 key range(一个 IDBKeyRange)。如果为 null 或未给定,则使用 unbounded key range。
openKeyCursor(query, direction) 方法的步骤是
-
令 transaction 为 this 的 transaction。
-
令 store 为 this 的 object store。
-
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 不是 active,则 throw 一个 "
TransactionInactiveError"DOMException。 -
令 range 为 converting a value to a key range 使用 query 的结果。重新抛出任何异常。
-
令 cursor 为一个新的 cursor,其 source handle 设置为 this,position 为 undefined,direction 设置为 direction,got value flag 设置为 false,key 和 value 为 undefined,range 设置为 range,且 key only flag 设置为 true。
-
令 operation 为运行 iterate a cursor 的算法,参数为 the current Realm record 和 cursor。
-
令 request 为运行 asynchronously execute a request 的结果,参数为 this 和 operation。
-
将 cursor 的 request 设置为 request。
-
返回 request。
注意: query 参数可以是标识用作 cursor 的 range 的 key 或 key range(一个 IDBKeyRange)。如果为 null 或未给定,则使用 unbounded key range。
- index = store . index(name)
- index = store .
createIndex(name, keyPath [, options]) -
在 store 中创建一个具有给定 name、keyPath 和 options 的新 index,并返回一个新
IDBIndex。如果 keyPath 和 options 定义的约束无法通过 store 中已有的数据满足,则 upgrade transaction 将 abort 并抛出 "ConstraintError"DOMException。如果未在 upgrade transaction 中调用,则 throw 一个 "
InvalidStateError"DOMException。 - store .
deleteIndex(name) -
删除 store 中具有给定 name 的 index。
如果未在 upgrade transaction 中调用,则 throw 一个 "
InvalidStateError"DOMException。
createIndex(name, keyPath, options) 方法的步骤是
-
令 transaction 为 this 的 transaction。
-
令 store 为 this 的 object store。
-
如果 transaction 不是 upgrade transaction,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 不是 active,则 throw 一个 "
TransactionInactiveError"DOMException。 -
如果 store 中已存在一个 named name 的 index,则 throw 一个 "
ConstraintError"DOMException。 -
如果 keyPath 不是 valid key path,则 throw 一个 "
SyntaxError"DOMException。 -
令 unique 为 options 的
unique成员。 -
令 multiEntry 为 options 的
multiEntry成员。 -
如果 keyPath 是序列且 multiEntry 为 true,则 throw 一个 "
InvalidAccessError"DOMException。 -
令 index 为 store 中的一个新 index。将 index 的 name 设置为 name,key path 设置为 keyPath,unique flag 设置为 unique,以及 multiEntry flag 设置为 multiEntry。
-
返回一个与 index 和 this 关联的新 index handle。
此方法在 object store 中创建并返回一个具有给定名称的新 index。注意,此方法必须仅在 upgrade transaction 中调用。
请求创建的索引可以包含对该索引 referenced object store 中允许数据的约束,例如要求索引 key path 所引用的值具有唯一性。如果 referenced object store 中已包含违反这些约束的数据,这不得导致 createIndex() 的实现抛出异常或影响其返回值。实现仍必须创建并返回一个 IDBIndex 对象,并且实现必须 queue a database task 来中止用于 createIndex() 调用的 upgrade transaction。
此方法同步修改调用它的 IDBObjectStore 实例上的 indexNames 属性。尽管此方法不返回 IDBRequest 对象,但索引创建本身在 upgrade transaction 中作为异步请求处理。
在某些实现中,createIndex() 方法返回后,实现可能会异步遇到索引创建问题。例如,在有关新创建索引的元数据排队等待异步插入数据库的实现中,或者实现因配额原因需要询问用户权限的情况下。此类实现仍必须创建并返回一个 IDBIndex 对象,并且一旦实现确定索引创建失败,它必须使用适当的错误运行 abort a transaction 的步骤。例如,如果由于配额原因导致创建 index 失败,则必须使用 QuotaExceededError 作为错误;如果由于 unique flag 约束导致无法创建索引,则必须使用 "ConstraintError" DOMException 作为错误。
索引的异步创建在以下示例中是可观察到的
const request1= objectStore. put({ name: "betty" }, 1 ); const request2= objectStore. put({ name: "betty" }, 2 ); const index= objectStore. createIndex( "by_name" , "name" , { unique: true });
在调用 createIndex() 的位置,两个 requests 均未执行。当第二个请求执行时,创建了一个重复名称。由于索引创建被视为异步 request,索引的 uniqueness constraint 不会导致第二个 request 失败。相反,当索引创建且约束失败时,transaction 将被 aborted。
index(name) 方法的步骤是
-
令 transaction 为 this 的 transaction。
-
令 store 为 this 的 object store。
-
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 为 finished,则 throw 一个 "
InvalidStateError"DOMException。 -
令 index 为 this 的 index set 中 named name 的 index(如果存在),否则 throw 一个 "
NotFoundError"DOMException。 -
返回一个与 index 和 this 关联的 index handle。
注意: 在同一 IDBObjectStore 实例上使用相同名称调用此方法的每次调用都会返回相同的 IDBIndex 实例。
注意: 返回的 IDBIndex 实例特定于此 IDBObjectStore 实例。如果在不同的 IDBObjectStore 实例上使用相同名称调用此方法,则会返回不同的 IDBIndex 实例。
deleteIndex(name) 方法的步骤是
-
令 transaction 为 this 的 transaction。
-
令 store 为 this 的 object store。
-
如果 transaction 不是 upgrade transaction,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 store 已被删除,则 throw 一个 "
InvalidStateError"DOMException。 -
如果 transaction 的 state 不是 active,则 throw 一个 "
TransactionInactiveError"DOMException。 -
令 index 为 store 中 named name 的 index(如果存在),否则 throw 一个 "
NotFoundError"DOMException。 -
销毁 index。
此方法销毁 object store 中具有给定名称的 index。注意,此方法必须仅在 upgrade transaction 中调用。
此方法同步修改调用它的 IDBObjectStore 实例上的 indexNames 属性。尽管此方法不返回 IDBRequest 对象,但索引销毁本身在 upgrade transaction 中作为异步请求处理。
4.6. IDBIndex 接口
IDBIndex 接口表示一个 index handle。
[Exposed =(Window ,Worker )]interface {IDBIndex attribute DOMString name ; [SameObject ]readonly attribute IDBObjectStore objectStore ;readonly attribute any keyPath ;readonly attribute boolean multiEntry ;readonly attribute boolean unique ; [NewObject ]IDBRequest get (any ); [query NewObject ]IDBRequest getKey (any ); [query NewObject ]IDBRequest getAll (optional any ,queryOrOptions optional [EnforceRange ]unsigned long ); [count NewObject ]IDBRequest getAllKeys (optional any ,queryOrOptions optional [EnforceRange ]unsigned long ); [count NewObject ]IDBRequest getAllRecords (optional IDBGetAllOptions = {}); [options NewObject ]IDBRequest count (optional any ); [query NewObject ]IDBRequest openCursor (optional any ,query optional IDBCursorDirection = "next"); [direction NewObject ]IDBRequest openKeyCursor (optional any ,query optional IDBCursorDirection = "next"); };direction
- index .
name -
返回索引的 name。
- index .
name= newName -
将 store 的 name 更新为 newName。
如果未在 upgrade transaction 中调用,则 throw 一个 "
InvalidStateError"DOMException。 - index .
objectStore -
返回该索引所属的
IDBObjectStore。 - index . keyPath
-
返回该索引的 key path。
- index . multiEntry
-
如果索引的 multiEntry flag 为 true,则返回 true。
- index . unique
-
如果索引的 unique flag 为 true,则返回 true。
name getter 的步骤是返回 this 的 name。
这是否与 index 的 name 相同?
只要 transaction 未 finished,这就与关联的 index 的 name 相同。但一旦 transaction finished,此属性将不会反映后续 upgrade transaction 所做的更改。name setter 的步骤是
-
令 name 为 the given value。
-
令 transaction 为 this 的 transaction。
-
如果 transaction 不是 升级事务,则 抛出 "
InvalidStateError"DOMException。 -
如果 transaction 的 状态 不是 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
如果 index 或 index 的 对象仓库 已被删除,则 抛出 "
InvalidStateError"DOMException。 -
如果 index 的 名称 等于 name,则终止这些步骤。
-
如果 index 的 对象仓库 中已经存在一个 命名为 name 的 索引,则 抛出 "
ConstraintError"DOMException。 -
将 index 的 名称 设置为 name。
objectStore 获取器步骤是返回 this 的 对象仓库句柄。
keyPath 获取器步骤是返回 this 的 索引 的 键路径。根据 [WEBIDL],该 键路径 将转换为 DOMString(如果是一个字符串)或 sequence<(如果是一个字符串序列)。DOMString>
注意: 返回的值并非创建 索引 时使用的同一个实例。然而,如果该属性返回一个对象(特别是 Array),它每次被检查时都会返回同一个对象实例。更改该对象的属性不会对 索引 产生影响。
multiEntry 获取器步骤是返回 this 的 索引 的 多重条目标志。
unique 获取器步骤是返回 this 的 索引 的 唯一性标志。
TransactionInactiveError" DOMException。- request = index .
get(query) - request = index .
getKey(query) - request = index .
getAll(query [, count]) - request = index .
getAll({query, count, direction}) -
检索 query 中给定的 键 或 键范围 匹配的 记录 的 值(如果给定了 count,则最多检索 count 条)。将 direction 选项设置为 "
next" 以检索前 count 个值,设置为 "prev" 以返回最后 count 个值。将 direction 选项设置为 "nextunique" 或 "prevunique",可在检索到具有重复索引键的第一个记录后,排除具有相同索引键的记录。 - request = index .
getAllKeys(query [, count]) - request = index .
getAllKeys({query, count, direction}) -
检索 query 中给定的 键 或 键范围 匹配的 记录 的 键(如果给定了 count,则最多检索 count 条)。将 direction 选项设置为 "
next" 以检索前 count 个键,设置为 "prev" 以返回最后 count 个键。将 direction 选项设置为 "nextunique" 或 "prevunique",可在检索到具有重复索引键的第一个记录后,排除具有相同索引键的记录。 - request = index .
getAllRecords({query, count, direction}) -
query 选项指定要匹配的 键 或 键范围。count 选项限制匹配记录的数量。将 direction 选项设置为 "
next" 以检索前 count 条记录,设置为 "prev" 以返回最后 count 条记录。将 direction 选项设置为 "nextunique" 或 "prevunique",可在检索到具有重复索引键的第一个记录后,排除具有相同索引键的记录。如果成功,request 的
result将为一个Array,其每个成员都是一个IDBRecord。使用IDBRecord 的 key获取记录的索引 键。使用IDBRecord 的 primaryKey获取记录的 键。 - request = index .
count(query) -
检索 query 中给定的 键 或 键范围 匹配的 记录 的数量。
如果成功,request 的
result将为计数值。
get(query) 方法步骤是
-
如果 index 或 index 的 对象仓库 已被删除,则 抛出 "
InvalidStateError"DOMException。 -
如果 transaction 的 状态 不是 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
令 range 为 将值转换为键范围(使用 query 和 true)的结果。重新抛出任何异常。
-
令 operation 为运行 从索引中检索引用值 算法,该算法使用 当前 Realm 记录、index 和 range。
-
返回运行 异步执行请求 的结果(一个
IDBRequest),该操作使用 this 和 operation。
注意: query 参数可以是标识要检索的 引用值 的 键 或 键范围(一个 IDBKeyRange)。如果指定了范围,该方法会检索该范围内第一个存在的记录。
注意: 当给定键的记录不存在时,此方法产生的结果与记录存在但值为 undefined 时相同。如果您需要区分这两种情况,可以使用带有相同键的 openCursor()。如果记录存在,这将返回一个值为 undefined 的游标;如果不存在此类记录,则不返回任何游标。
getKey(query) 方法步骤是
-
如果 index 或 index 的 对象仓库 已被删除,则 抛出 "
InvalidStateError"DOMException。 -
如果 transaction 的 状态 不是 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
令 range 为 将值转换为键范围(使用 query 和 true)的结果。重新抛出任何异常。
-
令 operation 为运行 从索引中检索值 算法,该算法使用 index 和 range。
-
返回运行 异步执行请求 的结果(一个
IDBRequest),该操作使用 this 和 operation。
注意: query 参数可以是标识要检索的 记录 键的 键 或 键范围(一个 IDBKeyRange)。如果指定了范围,该方法会检索该范围内第一个存在的键。
getAll(queryOrOptions, count) 方法步骤是
-
返回运行 创建检索多个项目的请求 的结果,该算法使用 当前 Realm 记录、this、"value"、queryOrOptions 和(如果给定)count。重新抛出任何异常。
getAllKeys(queryOrOptions, count) 方法步骤是
-
返回运行 创建检索多个项目的请求 的结果,该算法使用 当前 Realm 记录、this、"key"、queryOrOptions 和(如果给定)count。重新抛出任何异常。
getAllRecords(options) 方法步骤是
-
返回运行 创建检索多个项目的请求 的结果,该算法使用 当前 Realm 记录、this、"record" 和 options。重新抛出任何异常。
count(query) 方法步骤是
-
如果 index 或 index 的 对象仓库 已被删除,则 抛出 "
InvalidStateError"DOMException。 -
如果 transaction 的 状态 不是 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
令 range 为 将值转换为键范围(使用 query)的结果。重新抛出任何异常。
-
令 operation 为运行 计算范围内记录数 算法,该算法使用 index 和 range。
-
返回运行 异步执行请求 的结果(一个
IDBRequest),该操作使用 this 和 operation。
注意: query 参数可以是标识要计算的 记录 的 键 或 键范围(一个 IDBKeyRange)。如果为 null 或未给出,则使用 无界键范围。
TransactionInactiveError" DOMException。- request = index .
openCursor([query [, direction = "next"]]) -
在与 query 匹配的 记录 上打开一个按 direction 排序的 游标。如果 query 为 null,则匹配 index 中的所有 记录。
如果成功,request 的
result将为IDBCursorWithValue,若没有匹配的 记录,则为 null。 - request = index .
openKeyCursor([query [, direction = "next"]]) -
在与 query 匹配的 记录 上打开一个按 direction 排序且 仅键标志 设置为 true 的 游标。如果 query 为 null,则匹配 index 中的所有 记录。
openCursor(query, direction) 方法步骤是
-
如果 index 或 index 的 对象仓库 已被删除,则 抛出 "
InvalidStateError"DOMException。 -
如果 transaction 的 状态 不是 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
令 range 为 将值转换为键范围(使用 query)的结果。重新抛出任何异常。
-
令 cursor 为一个新的 游标,其 源句柄 设置为 this,位置 为 undefined,方向 设置为 direction,获取值标志 设置为 false,键 和 值 为 undefined,范围 设置为 range,仅键标志 设置为 false。
-
令 operation 为运行 遍历游标 算法,该算法使用 当前 Realm 记录 和 cursor。
-
将 cursor 的 请求 设置为 request。
-
返回 request。
注意: query 参数可以是标识作为 游标 的 范围 的 键 或 键范围(一个 IDBKeyRange)。如果为 null 或未给出,则使用 无界键范围。
openKeyCursor(query, direction) 方法步骤是
-
如果 index 或 index 的 对象仓库 已被删除,则 抛出 "
InvalidStateError"DOMException。 -
如果 transaction 的 状态 不是 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
令 range 为 将值转换为键范围(使用 query)的结果。重新抛出任何异常。
-
令 cursor 为一个新的 游标,其 源句柄 设置为 this,位置 为 undefined,方向 设置为 direction,获取值标志 设置为 false,键 和 值 为 undefined,范围 设置为 range,仅键标志 设置为 true。
-
令 operation 为运行 遍历游标 算法,该算法使用 当前 Realm 记录 和 cursor。
-
将 cursor 的 请求 设置为 request。
-
返回 request。
注意: query 参数可以是标识作为 游标 的 范围 的 键 或 键范围(一个 IDBKeyRange)。如果为 null 或未给出,则使用 无界键范围。
4.7. IDBKeyRange 接口
IDBKeyRange 接口表示一个 键范围。
[Exposed =(Window ,Worker )]interface {IDBKeyRange readonly attribute any lower ;readonly attribute any upper ;readonly attribute boolean lowerOpen ;readonly attribute boolean upperOpen ; // Static construction methods: [NewObject ]static IDBKeyRange only (any ); [value NewObject ]static IDBKeyRange lowerBound (any ,lower optional boolean =open false ); [NewObject ]static IDBKeyRange upperBound (any ,upper optional boolean =open false ); [NewObject ]static IDBKeyRange bound (any ,lower any ,upper optional boolean =lowerOpen false ,optional boolean =upperOpen false );boolean includes (any ); };key
lower 获取器步骤是返回 将键转换为值(使用 this 的 下界,如果其不为 null,否则为 undefined)的结果。
upper 获取器步骤是返回 将键转换为值(使用 this 的 上界,如果其不为 null,否则为 undefined)的结果。
lowerOpen 获取器步骤是返回 this 的 下界开启标志。
upperOpen 获取器步骤是返回 this 的 上界开启标志。
- range =
IDBKeyRange.only(key) -
返回一个新的
IDBKeyRange,仅覆盖 key。 - range =
IDBKeyRange.lowerBound(key [, open = false]) -
返回一个新的
IDBKeyRange,从 key 开始且没有上界。如果 open 为 true,则 key 不包含在范围内。 - range =
IDBKeyRange.upperBound(key [, open = false]) -
返回一个新的
IDBKeyRange,没有下界且以 key 结束。如果 open 为 true,则 key 不包含在范围内。 - range =
IDBKeyRange.bound(lower, upper [, lowerOpen = false [, upperOpen = false]]) -
返回一个新的
IDBKeyRange,覆盖从 lower 到 upper 的范围。如果 lowerOpen 为 true,则 lower 不包含在范围内。如果 upperOpen 为 true,则 upper 不包含在范围内。
only(value) 方法步骤是
lowerBound(lower, open) 方法步骤是
upperBound(upper, open) 方法步骤是
bound(lower, upper, lowerOpen, upperOpen) 方法步骤是
-
令 lowerKey 为 将值转换为键(使用 lower)的结果。重新抛出任何异常。
-
如果 lowerKey 是 "invalid value" 或 "invalid type",则 抛出 "
DataError"DOMException。 -
令 upperKey 为 将值转换为键(使用 upper)的结果。重新抛出任何异常。
-
如果 upperKey 是 "invalid value" 或 "invalid type",则 抛出 "
DataError"DOMException。 -
如果 lowerKey 大于 upperKey,则 抛出 "
DataError"DOMException。 -
创建并返回一个新 键范围,其 下界 设置为 lowerKey,下界开启标志 设置为 lowerOpen,上界 设置为 upperKey,上界开启标志 设置为 upperOpen。
- range .
includes(key) -
如果 key 包含在范围内,则返回 true,否则返回 false。
includes(key) 方法步骤是
-
令 k 为 将值转换为键(使用 key)的结果。重新抛出任何异常。
-
如果 k 是 "invalid value" 或 "invalid type",则 抛出 "
DataError"DOMException。 -
如果 k 处于 本范围内,则返回 true,否则返回 false。
4.8. IDBRecord 接口
[Exposed =(Window ,Worker )]interface {IDBRecord readonly attribute any key ;readonly attribute any primaryKey ;readonly attribute any value ; };
key 获取器步骤是返回 将键转换为值(使用 this 的 键)的结果。
primaryKey 获取器步骤是返回 将键转换为值(使用 this 的 主键)的结果。
4.9. IDBCursor 接口
游标 对象实现 IDBCursor 接口。对于给定的 游标,始终只有一个代表它的 IDBCursor 实例。同时可以使用的游标数量没有限制。
[Exposed =(Window ,Worker )]interface {IDBCursor readonly attribute (IDBObjectStore or IDBIndex )source ;readonly attribute IDBCursorDirection direction ;readonly attribute any key ;readonly attribute any primaryKey ; [SameObject ]readonly attribute IDBRequest request ;undefined advance ([EnforceRange ]unsigned long );count undefined continue (optional any );key undefined continuePrimaryKey (any ,key any ); [primaryKey NewObject ]IDBRequest update (any ); [value NewObject ]IDBRequest delete (); };enum {IDBCursorDirection ,"next" ,"nextunique" ,"prev" };"prevunique"
- cursor .
source -
返回打开该游标的
IDBObjectStore或IDBIndex。 - cursor .
direction -
返回游标的 方向("
next"、"nextunique"、"prev" 或 "prevunique")。 - cursor .
key -
返回游标的 键。如果游标正在推进或已结束,则抛出 "
InvalidStateError"DOMException。 - cursor .
primaryKey -
返回游标的 有效键。如果游标正在推进或已结束,则抛出 "
InvalidStateError"DOMException。 - cursor .
request -
返回用于获取此游标的 请求。
注意: source 属性从不返回 null 或抛出异常,即使游标当前正在被遍历、已遍历至末尾,或者其 事务 不 活跃。
key 获取器步骤是返回 将键转换为值(使用游标当前的 键)的结果。
注意: 如果 key 返回一个对象(例如 Date 或 Array),它每次被检查时都会返回同一个对象实例,直到游标的 键 发生改变。这意味着如果对象被修改,任何检查游标值的人都会看到这些修改。然而,修改此类对象不会修改数据库的内容。
primaryKey 获取器步骤是返回 将键转换为值(使用游标当前的 有效键)的结果。
注意: 如果 primaryKey 返回一个对象(例如 Date 或 Array),它每次被检查时都会返回同一个对象实例,直到游标的 有效键 发生改变。这意味着如果对象被修改,任何检查游标值的人都会看到这些修改。然而,修改此类对象不会修改数据库的内容。
request 属性在本版本中新增。它在 Chrome 76、Edge 79、Firefox 77 和 Safari 15 中得到支持。🚧IDBRequest 上触发一个 success 事件。如果范围内有 记录,result 将为同一个游标,否则为 undefined。如果游标正在推进时调用,将抛出 "InvalidStateError" DOMException。
如果在 事务 不 活跃 时调用,以下方法会抛出 "TransactionInactiveError" DOMException。
- cursor .
advance(count) -
将游标向前推进范围内的下 count 条 记录。
- cursor .
continue() -
将游标向前推进范围内的下一条 记录。
- cursor .
continue(key) -
将游标向前推进范围内匹配或之后 key 的下一条 记录。
- cursor .
continuePrimaryKey(key, primaryKey) -
将游标向前推进范围内匹配或之后 key 和 primaryKey 的下一条 记录。如果 source 不是 索引,则抛出 "
InvalidAccessError"DOMException。
advance(count) 方法步骤是
-
如果 transaction 的 状态 不是 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
如果 this 的 source 或 有效对象仓库 已被删除,则 抛出 "
InvalidStateError"DOMException。 -
如果 this 的 获取值标志 为 false(表示游标正在被遍历或已遍历至末尾),则 抛出 "
InvalidStateError"DOMException。 -
将 request 的 处理标志 设置为 false。
-
将 request 的 完成标志 设置为 false。
-
令 operation 为运行 遍历游标 算法,该算法使用 当前 Realm 记录、this 和 count。
注意: 在新的游标数据加载之前多次调用此方法(例如在同一个 onsuccess 处理程序中两次调用 advance()),会导致第二次调用时抛出 "InvalidStateError" DOMException,因为游标的 获取值标志 已被设置为 false。
continue(key) 方法步骤是
-
如果 transaction 的 状态 不是 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
如果 this 的 source 或 有效对象仓库 已被删除,则 抛出 "
InvalidStateError"DOMException。 -
如果 this 的 获取值标志 为 false(表示游标正在被遍历或已遍历至末尾),则 抛出 "
InvalidStateError"DOMException。 -
如果给定了 key,则
-
将 request 的 处理标志 设置为 false。
-
将 request 的 完成标志 设置为 false。
-
令 operation 为运行 遍历游标 算法,该算法使用 当前 Realm 记录、this 和 key(如果给定)。
注意: 在新的游标数据加载之前多次调用此方法(例如在同一个 onsuccess 处理程序中两次调用 continue()),会导致第二次调用时抛出 "InvalidStateError" DOMException,因为游标的 获取值标志 已被设置为 false。
continuePrimaryKey(key, primaryKey) 方法步骤是
-
如果 transaction 的 状态 不是 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
如果 this 的 source 或 有效对象仓库 已被删除,则 抛出 "
InvalidStateError"DOMException。 -
如果 this 的 source 不是 索引,则 抛出 "
InvalidAccessError"DOMException。 -
如果 this 的 方向 不是 "
next" 或 "prev",则 抛出 "InvalidAccessError"DOMException。 -
如果 this 的 获取值标志 为 false(表示游标正在被遍历或已遍历至末尾),则 抛出 "
InvalidStateError"DOMException。 -
令 r 为 将值转换为键(使用 key)的结果。重新抛出任何异常。
-
如果 r 是 "invalid value" 或 "invalid type",则 抛出 "
DataError"DOMException。 -
令 key 为 r。
-
令 r 为 将值转换为键(使用 primaryKey)的结果。重新抛出任何异常。
-
如果 r 是 "invalid value" 或 "invalid type",则 抛出 "
DataError"DOMException。 -
令 primaryKey 为 r。
-
如果 key 小于 this 的 位置,且 this 的 方向 是 "
next",则 抛出 "DataError"DOMException。 -
如果 key 大于 此 位置 且 此 方向 为 "
prev",则 抛出 "DataError"DOMException。 -
如果 key 等于 此 位置,且 primaryKey 小于 或 等于 此 对象仓库位置,且 此 方向 为 "
next",则 抛出 "DataError"DOMException。 -
如果 key 等于 此 位置,且 primaryKey 大于 或 等于 此 对象仓库位置,且 此 方向 为 "
prev",则 抛出 "DataError"DOMException。 -
将 request 的 处理标志 设置为 false。
-
将 request 的 完成标志 设置为 false。
注意: 在新的游标数据加载之前多次调用此方法——例如,在同一个 onsuccess 处理程序中两次调用 continuePrimaryKey()——会导致第二次调用时抛出 "InvalidStateError" DOMException,因为游标的 获取值标志 已被设置为 false。
ReadOnlyError" DOMException;如果调用时 事务 不 活跃,则抛出 "TransactionInactiveError" DOMException。
update(value) 方法的步骤如下
-
如果 transaction 的 状态 不 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
如果 transaction 是 只读事务,抛出 "
ReadOnlyError"DOMException。 -
如果 此 源 或 有效对象仓库 已被删除,抛出 "
InvalidStateError"DOMException。 -
如果 此 获取值标志 为 false(表示游标正在遍历或已遍历完),抛出 "
InvalidStateError"DOMException。 -
如果 此 仅键标志 为 true,抛出 "
InvalidStateError"DOMException。 -
设 targetRealm 为用户代理定义的 领域 (Realm)。
-
设 clone 为 value 在 targetRealm 和 transaction 期间的 克隆。重新抛出任何异常。
为什么要创建值的副本?
值在存储时会被序列化。在此将其视为副本允许规范中的其他算法将其视为 ECMAScript 值,但如果行为差异不可观察,实现可以对其进行优化。 -
-
设 kpk 为使用 从值中使用键路径提取键 的算法处理 clone 和 此 有效对象仓库 的 键路径 的结果。重新抛出任何异常。
-
-
设 operation 为运行 将记录存入对象仓库 的算法,包含 此 有效对象仓库、clone、此 有效键 和 false。
-
返回使用 此 和 operation 运行 异步执行请求 的结果(一个
IDBRequest)。
注意: 将记录存入对象仓库 的结果是:如果该记录在游标移动到它之后被删除,则会创建一个新记录。
delete() 方法的步骤如下
-
如果 transaction 的 状态 不 活跃,则 抛出 "
TransactionInactiveError"DOMException。 -
如果 transaction 是 只读事务,抛出 "
ReadOnlyError"DOMException。 -
如果 此 源 或 有效对象仓库 已被删除,抛出 "
InvalidStateError"DOMException。 -
如果 此 获取值标志 为 false(表示游标正在遍历或已遍历完),抛出 "
InvalidStateError"DOMException。 -
如果 此 仅键标志 为 true,抛出 "
InvalidStateError"DOMException。 -
设 operation 为运行 从对象仓库中删除记录 的算法,包含 此 有效对象仓库 和 此 有效键。
-
返回使用 此 和 operation 运行 异步执行请求 的结果(一个
IDBRequest)。
将 仅键标志 设置为 false 的 游标 同时实现了 IDBCursorWithValue 接口。
[Exposed =(Window ,Worker )]interface :IDBCursorWithValue IDBCursor {readonly attribute any value ; };
注意: 如果 value 返回一个对象,每次检查它时都会返回相同的对象实例,直到游标的 值 发生改变。这意味着如果该对象被修改,任何检查游标值的人都会看到这些修改。但是,修改此类对象不会修改数据库的内容。
4.10. IDBTransaction 接口
事务 对象实现以下接口
[Exposed =(Window ,Worker )]interface :IDBTransaction EventTarget {readonly attribute DOMStringList objectStoreNames ;readonly attribute IDBTransactionMode mode ;readonly attribute IDBTransactionDurability durability ; [SameObject ]readonly attribute IDBDatabase db ;readonly attribute DOMException ?error ;IDBObjectStore objectStore (DOMString );name undefined commit ();undefined abort (); // Event handlers:attribute EventHandler onabort ;attribute EventHandler oncomplete ;attribute EventHandler onerror ; };enum {IDBTransactionMode ,"readonly" ,"readwrite" };"versionchange"
- transaction .
objectStoreNames - transaction .
mode -
返回创建该事务时所使用的 模式("
readonly" 或 "readwrite"),或者对于 升级事务,返回 "versionchange"。 - transaction .
durability - transaction .
db -
返回该事务的 连接。
- transaction .
error -
如果事务被 终止,则返回提供原因的错误(一个
DOMException)。
注意: 此属性返回的每个列表的内容不会改变,但在 升级事务 期间对该属性的后续调用可能会返回不同内容的列表,因为 对象仓库 会被创建和删除。
durability 获取器的步骤是返回 此 的 持久性提示。
durability 属性是本版本新增的。它在 Chrome 82、Edge 82、Firefox 126 和 Safari 15 中受支持。 🚧error 获取器的步骤是返回 此 的 错误,若无则返回 null。
注意: 如果此 事务 是由于失败的 请求 而终止的,则该错误将与该 请求 的 错误 相同。如果此 事务 是由于事件处理程序中的未捕获异常而终止的,则错误将是一个 "AbortError" DOMException。如果 事务 是由于提交过程中的错误而终止的,它将反映失败的原因(例如 QuotaExceededError,或 "ConstraintError" 或 "UnknownError" DOMException)。
- transaction .
objectStore(name) -
返回 事务 范围 内的一个
IDBObjectStore。 - transaction .
abort() -
终止事务。所有待处理的 请求 都将失败并抛出 "
AbortError"DOMException,所有对数据库所做的更改都将被撤销。 - transaction .
commit() -
尝试提交事务。所有待处理的 请求 都将被允许完成,但不会接受任何新的请求。此方法可用于强制事务快速结束,而无需在尝试正常提交之前等待待处理请求触发
success事件。如果待处理请求失败(例如由于约束错误),事务将会终止。成功请求的
success事件仍会触发,但在事件处理程序中抛出异常不会导致事务终止。同样,失败请求的error事件仍会触发,但调用preventDefault()将无法阻止事务终止。
objectStore(name) 方法的步骤如下
-
如果 此 的 状态 为 已完成,则 抛出 "
InvalidStateError"DOMException。 -
设 store 为 此 范围 中 名为 name 的 对象仓库;如果不存在,则 抛出 "
NotFoundError"DOMException。
注意: 在同一个 IDBTransaction 实例上使用相同名称调用此方法,每次都会返回同一个 IDBObjectStore 实例。
注意: 返回的 IDBObjectStore 实例特定于此 IDBTransaction。如果此方法在不同的 IDBTransaction 上调用,则会返回不同的 IDBObjectStore 实例。
abort() 方法的步骤如下
-
如果 此 的 状态 为 正在提交 或 已完成,则 抛出 "
InvalidStateError"DOMException。
commit() 方法的步骤如下
-
如果 此 的 状态 不 活跃,则 抛出 "
InvalidStateError"DOMException。
commit() 方法是本版本新增的。它在 Chrome 76、Edge 79、Firefox 74 和 Safari 15 中受支持。 🚧注意: 通常没有必要在 事务 上调用 commit()。当所有未完成的请求都已满足且没有发出新请求时,事务将自动提交。此调用可用于在不等待分发来自未完成 请求 的事件的情况下启动 提交 过程。
onabort 属性是一个 事件处理程序 IDL 属性,其 事件处理程序事件类型 为 abort。
oncomplete 属性是一个 事件处理程序 IDL 属性,其 事件处理程序事件类型 为 complete。
onerror 属性是一个 事件处理程序 IDL 属性,其 事件处理程序事件类型 为 error。
注意: 要确定 事务 是否已成功完成,请监听 事务 的 complete 事件,而不是特定 请求 的 success 事件,因为在 success 事件触发后,事务 仍可能失败。
5. 算法
5.1. 打开数据库连接
要 打开数据库连接(使用已请求打开 数据库 的 storageKey、数据库 name、数据库 version 和 request),请执行以下步骤
-
设 queue 为 storageKey 和 name 的 连接队列。
-
将 request 添加到 queue 中。
-
等待直到 queue 中的所有先前请求均已处理完毕。
-
如果 version 未定义,则在 db 为 null 时设 version 为 1,否则设为 db 的 版本。
-
如果 db 为 null,设 db 为一个新的 数据库,其 名称 为 name,版本 为 0,且没有 对象仓库。如果因任何原因失败,则返回适当的错误(例如
QuotaExceededError或 "UnknownError"DOMException)。 -
如果 db 的 版本 大于 version,则返回一个新 创建的 "
VersionError"DOMException并终止这些步骤。 -
设 connection 为与 db 的新 连接。
-
设置 connection 的 版本 为 version。
-
如果 db 的 版本 小于 version,则
-
对于 openConnections 中的每个 entry(其 待关闭标志 未设置为 true),排队一个数据库任务,以在 entry 上 触发一个版本变更事件,事件名为
versionchange,并携带 db 的 版本 和 version。注意: 触发此事件可能会导致 openConnections 中的一个或多个其他对象被关闭,在这种情况下,即使尚未完成操作,也不会在这些对象上触发
versionchange事件。 -
等待所有事件触发。
-
如果 openConnections 中的任何 连接 仍未关闭,则 排队一个数据库任务,以在 request 上 触发一个版本变更事件,事件名为
blocked,并携带 db 的 版本 和 version。 -
使用 connection、version 和 request 运行 升级数据库。
-
如果 connection 已 关闭,则返回一个新 创建的 "
AbortError"DOMException并终止这些步骤。 -
如果设置了 request 的 错误,则运行 关闭数据库连接 的步骤(使用 connection),返回一个新 创建的 "
AbortError"DOMException并终止这些步骤。
-
返回 connection。
5.2. 关闭数据库连接
要 关闭数据库连接(使用 connection 对象和一个可选的 forced flag),请执行以下步骤
注意: 一旦 连接 的 待关闭标志 被设置为 true,就不能再使用该 连接 创建 新事务。所有 创建 事务的方法都会首先检查 连接 的 待关闭标志,如果为 true,则抛出异常。
注意: 一旦 连接 关闭,这可以取消 升级数据库 和 删除数据库 的步骤的阻塞,这两个步骤 均 等待 对给定 数据库 的 连接 在继续之前全部关闭。
5.3. 删除数据库
要 删除数据库(使用请求删除 数据库 的 storageKey、数据库 name 和 request),请执行以下步骤
-
设 queue 为 storageKey 和 name 的 连接队列。
-
将 request 添加到 queue 中。
-
等待直到 queue 中的所有先前请求均已处理完毕。
-
对于 openConnections 中的每个 entry(其 待关闭标志 未设置为 true),排队一个数据库任务,以在 entry 上 触发一个版本变更事件,事件名为
versionchange,并携带 db 的 版本 和 null。注意: 触发此事件可能会导致 openConnections 中的一个或多个其他对象被关闭,在这种情况下,即使尚未完成操作,也不会在这些对象上触发
versionchange事件。 -
等待所有事件触发。
-
如果 openConnections 中的任何 连接 仍未关闭,则 排队一个数据库任务,以在 request 上 触发一个版本变更事件,事件名为
blocked,并携带 db 的 版本 和 null。 -
设 version 为 db 的 版本。
-
删除 db。如果因任何原因失败,则返回适当的错误(例如
QuotaExceededError或 "UnknownError"DOMException)。 -
返回 version。
5.4. 提交事务
要 提交事务(使用待提交的 transaction),请执行以下步骤
-
在 并行中 运行以下步骤:
-
如果写入数据库的过程中发生错误,则运行 终止事务(使用 transaction 和适当的错误类型,例如
QuotaExceededError或 "UnknownError"DOMException),并终止这些步骤。 -
排队一个数据库任务以运行以下步骤
5.5. 终止事务
要 终止事务(使用待终止的 transaction 和 error),请执行以下步骤
-
事务 对 数据库 所做的所有更改均被撤销。对于 升级事务,这包括对 对象仓库 和 索引 集合的更改,以及对 版本 的更改。在事务期间创建的任何 对象仓库 和 索引 现在在其他算法看来均被视为已删除。
-
设置 transaction 的 错误 为 error。
-
对于 transaction 的 请求列表 中的每个 request,终止 异步执行请求 的步骤,设置 request 的 处理标志 为 true,并 排队一个数据库任务以运行以下步骤
-
设置 request 的 完成标志 为 true。
-
设置 request 的 结果 为 undefined。
-
设置 request 的 错误 为一个新 创建的 "
AbortError"DOMException。 -
触发一个事件,事件名为
error,作用于 request,其bubbles和cancelable属性初始化为 true。
注意: 这并不总是导致触发任何
error事件。例如,如果事务由于在 提交 事务期间出错而被终止,或者如果它是最后一个失败的剩余请求。 -
-
排队一个数据库任务以运行以下步骤
5.6. 异步执行 请求
要 异步执行请求(使用 source 对象、要对数据库执行的 operation 和一个可选的 request),请执行以下步骤
如果创建 request 所属的 事务 已 终止(使用 终止事务 的步骤),则可以随时终止这些步骤。
-
设 transaction 为与 source 关联的 事务。
-
将 request 添加到 transaction 的 请求列表 的末尾。
-
在 并行 中运行这些步骤
-
返回 request。
5.7. 升级数据库
要 升级数据库(使用 connection、新 version 和 request),请执行以下步骤
-
设 db 为 connection 的 数据库。
-
设置 db 的 升级事务 为 transaction。
-
启动 transaction。
-
设 old version 为 db 的 版本。
-
设置 request 的 处理标志 为 true。
-
排队一个数据库任务以运行以下步骤
-
设置 request 的 结果 为 connection。
-
设置 request 的 事务 为 transaction。
-
将 request 的 完成标记(done flag) 设置为 true。
-
将 transaction 的 状态 设置为 活跃(active)。
-
令 didThrow 为在 request 上以 old version 和 version 触发名为
upgradeneeded的 版本变更事件 的结果。 -
-
将 transaction 的 状态 设置为 不活跃(inactive)。
-
如果 didThrow 为 true,使用 transaction 和一个新 创建的 "
AbortError"DOMException运行 中止事务(abort a transaction)。
-
-
-
等待 transaction 完成。
注意: 在 事务 的 生命周期 中调用的某些算法(例如 提交事务 和 中止事务 的步骤)包含了针对 升级事务 的特定步骤。
5.8. 中止升级事务
要使用 transaction 中止升级事务,请运行以下步骤
注意: 这些步骤由 中止事务 的步骤按需运行,它们会撤销对 数据库(包括关联的 对象存储空间 和 索引 的集合)以及对 版本 的更改。
-
令 connection 为 transaction 的 连接。
-
令 database 为 connection 的 数据库。
-
如果 database 之前存在,将 connection 的 版本 设置为 database 的 版本;如果 database 是新创建的,则设置为 0(零)。
注意: 这会撤销
IDBDatabase对象返回的version的值。 -
如果 database 之前存在,将 connection 的 对象存储空间集合 设置为 database 中的 对象存储空间 集合;如果 database 是新创建的,则设置为集合为空。
注意: 这会撤销
IDBDatabase对象返回的objectStoreNames的值。 -
对于与 transaction 关联的每个 对象存储空间句柄 handle,包括在 transaction 期间创建或删除的 对象存储空间
注意: 这会撤销相关的
IDBObjectStore对象返回的name和indexNames的值。这是如何观察到的?
尽管在 事务 中止后,脚本无法通过IDBTransaction实例上的objectStore()方法访问 对象存储空间,但它仍然可以持有指向IDBObjectStore实例的引用,从中可以查询name和indexNames属性。 -
对于与 transaction 关联的每个 索引句柄 handle,包括在 transaction 期间创建或删除的 索引
注意: IDBDatabase 实例的 name 属性不会被修改,即使被中止的 升级事务 是在创建新 数据库。
5.9. 触发成功事件
要对 request 触发成功事件,请运行以下步骤
-
将 event 的
type属性设置为 "success"。 -
将 event 的
bubbles和cancelable属性设置为 false。 -
令 transaction 为 request 的 事务。
-
令 legacyOutputDidListenersThrowFlag 初始为 false。
-
使用 legacyOutputDidListenersThrowFlag 在 request 上 派发 event。
-
-
如果 legacyOutputDidListenersThrowFlag 为 true,使用 transaction 和一个新 创建的 "
AbortError"DOMException运行 中止事务。
5.10. 触发错误事件
要对 request 触发错误事件,请运行以下步骤
-
将 event 的
type属性设置为 "error"。 -
将 event 的
bubbles和cancelable属性设置为 true。 -
令 transaction 为 request 的 事务。
-
令 legacyOutputDidListenersThrowFlag 初始为 false。
-
-
如果 legacyOutputDidListenersThrowFlag 为 true,则使用 transaction 和一个新 创建的 "
AbortError"DOMException运行 中止事务 并终止这些步骤。即使 event 的 取消标记(canceled flag) 为 false,此步骤也会执行。注意: 这意味着如果触发了错误事件且任何事件处理程序抛出了异常,transaction 的
error属性将被设置为AbortError而非 request 的 错误,即使从未调用preventDefault()。 -
如果 event 的 取消标记 为 false,则使用 transaction 和 request 的 错误 运行 中止事务,并终止这些步骤。
5.11. 克隆值
要 克隆 在 transaction 期间位于 targetRealm 中的 value,请运行以下步骤
5.12. 创建检索多个条目的请求
要 创建检索多个条目的 请求,从 对象存储空间 或 索引 中使用 targetRealm、sourceHandle、kind、queryOrOptions 以及可选的 count,请运行以下步骤
-
令 source 为来自 sourceHandle 的 索引 或 对象存储空间。如果 sourceHandle 是一个 索引句柄,则 source 是 该索引句柄关联的索引。否则,source 是 该对象存储空间句柄关联的对象存储空间。
-
如果 source 已被删除,抛出 一个 "
InvalidStateError"DOMException。 -
如果 source 是一个 索引 且其 对象存储空间 已被删除,抛出 一个 "
InvalidStateError"DOMException。 -
令 transaction 为 sourceHandle 的 事务。
-
如果 transaction 的 状态 不为 活跃,则 抛出 一个 "
TransactionInactiveError"DOMException。 -
令 range 为一个 键范围。
-
令 direction 为一个 游标方向。
-
如果运行 是否为潜在有效的键范围 且使用 queryOrOptions 的结果为 true,则
-
否则:
-
令 operation 为要运行的算法。
-
如果 source 是一个 索引,将 operation 设置为 从索引中检索多个条目 且使用 targetRealm、source、range、kind、direction 以及(如果给定)count。
-
否则,将 operation 设置为 从对象存储空间中检索多个条目 且使用 targetRealm、source、range、kind、direction 以及(如果给定)count。
-
返回运行 异步执行请求 且使用 sourceHandle 和 operation 的结果(一个
IDBRequest)。
注意: range 可以是一个 键 或 键范围(一个 IDBKeyRange),用于标识要检索的 记录 条目。如果为 null 或未给定,则使用 无界键范围。如果指定了 count 且范围内记录数量超过 count,则仅检索前 count 个记录。
6. 数据库操作
本节描述对 数据库 中 对象存储空间 和 索引 数据执行的各种操作。这些操作由 异步执行请求 的步骤运行。
注意: 下面操作步骤中对 StructuredDeserialize() 的调用可以断言不会抛出异常(如 ! 前缀所示),因为它们仅对之前的 StructuredSerializeForStorage() 输出进行操作。
6.1. 对象存储空间存储操作
要使用 store、value、可选的 key 和 无覆盖标记(no-overwrite flag) 将记录存储到对象存储空间,请运行以下步骤
-
如果 store 使用 键生成器,则
-
如果 key 为 undefined,则
-
令 key 为对 store 生成键 的结果。
-
如果 key 为失败,则此操作失败,产生 "
ConstraintError"DOMException。中止此算法,不再采取进一步步骤。 -
如果 store 还使用 行内键(in-line keys),则使用 value、key 和 store 的 键路径 运行 使用键路径将键注入值。
-
-
否则,使用 key 为 store 运行 可能更新键生成器。
-
-
如果这些步骤接收到了 无覆盖标记 且为 true,并且 store 中已存在键 等于 key 的 记录,则此操作失败,产生 "
ConstraintError"DOMException。中止此算法,不再采取进一步步骤。 -
如果 store 中已存在键 等于 key 的 记录,则使用 从对象存储空间中删除记录 将该 记录 从 store 中移除。
-
在 store 中存储一条记录,包含 key 作为其键,! StructuredSerializeForStorage(value) 作为其值。该记录被存储在对象存储空间的 记录列表 中,使得列表按记录的键以 升序 排序。
-
对于每个 引用 store 的 index
-
令 index key 为使用 value、index 的 键路径 和 index 的 multiEntry 标记 从值中提取键 的结果。
-
如果 index key 是异常、无效或失败,则不对 index 采取进一步操作,并继续为下一个索引运行这些步骤。
注意: 此步骤中抛出的异常不会被重新抛出。
-
如果 index 的 multiEntry 标记 为 false,或者如果 index key 不是 数组键,并且如果 index 已包含一个 记录,其 键 等于 index key,且 index 的 唯一标记 为 true,则此操作失败,产生 "
ConstraintError"DOMException。中止此算法,不再采取进一步步骤。 -
如果 index 的 multiEntry 标记 为 true 且 index key 是一个 数组键,如果 index 已包含一个 记录,其 键 等于 index key 的任何 子键(subkeys),且 index 的 唯一标记 为 true,则此操作失败,产生 "
ConstraintError"DOMException。中止此算法,不再采取进一步步骤。 -
如果 index 的 multiEntry 标记 为 false,或者如果 index key 不是一个 数组键,则在 index 中存储一条记录,包含 index key 作为其键,key 作为其值。该记录被存储在 index 的 记录列表 中,使得列表首先按记录的键,其次按记录的值,以 升序 排序。
-
如果 index 的 multiEntry 标记 为 true 且 index key 是一个 数组键,则对于 index key 的 子键 中的每个 subkey,在 index 中存储一条记录,包含 subkey 作为其键,key 作为其值。该记录被存储在 index 的 记录列表 中,使得列表首先按记录的键,其次按记录的值,以 升序 排序。
注意: 没有 子键 是合法的。在这种情况下,不会向索引中添加任何记录。
注意: 即使 子键 中的任何成员本身是一个 数组键,该成员也会直接用作索引记录的键。嵌套的 数组键 不会被扁平化或“解包”以产生多行;只有最外层的 数组键 会被使用。
-
-
返回 key。
6.2. 对象存储空间检索操作
要使用 targetRealm、store 和 range 从对象存储空间中检索值,请运行以下步骤。它们返回 undefined、一个 ECMAScript 值或一个错误(一个 DOMException)
-
如果未找到 record,则返回 undefined。
-
令 serialized 为 record 的 值。如果从底层存储读取值时发生错误,则返回一个新 创建的 "
NotReadableError"DOMException。 -
返回 ! StructuredDeserialize(serialized, targetRealm)。
要使用 store 和 range 从对象存储空间中检索键,请运行以下步骤
要使用 targetRealm、store、range、kind、direction 以及可选的 count 从对象存储空间中检索多个条目,请运行以下步骤
-
如果 count 未给定或为 0(零),则令 count 为无穷大。
-
令 records 为一个空的 记录列表。
-
如果 direction 为 "
next" 或 "nextunique",将 records 设置为 store 的 记录列表 中前 count 个其 键 在 range 之内的记录。 -
如果 direction 为 "
prev" 或 "prevunique",将 records 设置为 store 的 记录列表 中最后 count 个其 键 在 range 之内的记录。 -
令 list 为一个空 列表。
-
对于 records 中的每个 record,根据 kind 进行切换
- "key"(关键)
- "value"
-
-
令 serialized 为 record 的 值。
-
令 value 为 ! StructuredDeserialize(serialized, targetRealm)。
-
追加 value 到 list。
-
- "record"
-
返回 list。
6.3. 索引检索操作
要使用 targetRealm、index 和 range 从索引中检索引用的值,请运行以下步骤
要使用 index 和 range 从索引中检索值,请运行以下步骤
要使用 targetRealm、index、range、kind、direction 以及可选的 count 从索引中检索多个条目,请运行以下步骤
-
如果 count 未给定或为 0(零),则令 count 为无穷大。
-
令 records 为一个空的 记录列表。
-
根据 direction 进行切换
- "next"
- "nextunique"
- "prev"
- "prevunique"
-
令 list 为一个空 列表。
-
对于 records 中的每个 record,根据 kind 进行切换
- "key"(关键)
- "value"
-
-
令 serialized 为 record 的 引用值。
-
令 value 为 ! StructuredDeserialize(serialized, targetRealm)。
-
追加 value 到 list。
-
- "record"
-
返回 list。
6.4. 对象存储空间删除操作
要使用 store 和 range 从对象存储空间中删除记录,请运行以下步骤
6.5. 记录计数操作
要使用 source 和 range 统计范围内的记录数量,请运行以下步骤
-
令 count 为 source 的记录列表中键 在 range 之内的记录数量(如有)。
-
返回 count。
6.6. 对象存储空间清空操作
6.7. 游标迭代操作
要使用 targetRealm、cursor、可选的要迭代到的 key 和 primaryKey 以及可选的 count 迭代游标,请运行以下步骤
-
令 source 为 cursor 的 来源。
-
令 direction 为 cursor 的 方向。
-
断言:如果给定了 primaryKey,则 source 是一个 索引,且 direction 为 "
next" 或 "prev"。 -
令 records 为 source 中的 记录列表。
注意: records 始终按 键 升序 排序。如果 source 是一个 索引,records 还按 值 升序 次要排序(其中 索引 中的值是被引用 对象存储空间 中 记录 的 键)。
-
令 range 为 cursor 的 范围。
-
令 position 为 cursor 的 位置。
-
令 object store position 为 cursor 的 对象存储空间位置。
-
如果未给定 count,则令 count 为 1。
-
当 count 大于 0 时
-
根据 direction 进行切换
- "
next" -
令 found record 为 records 中满足以下所有要求的第一个记录
- "
nextunique" -
令 found record 为 records 中满足以下所有要求的第一个记录
- "
prev" -
令 found record 为 records 中满足以下所有要求的最后一个记录
- "
prevunique" -
令 temp record 为 records 中满足以下所有要求的最后一个记录
如果 temp record 已定义,令 found record 为 records 中第一个其 键 等于 temp record 的 键 的记录。
注意: 使用 "
prevunique" 迭代会访问与 "nextunique" 访问的相同的记录,但顺序相反。
- "
-
如果 found record 未定义,则
-
令 position 为 found record 的键。
-
如果 source 是一个 索引,令 object store position 为 found record 的值。
-
将 count 减少 1。
-
-
将 cursor 的 位置 设置为 position。
-
如果 source 是一个 索引,将 cursor 的 对象存储空间位置 设置为 object store position。
-
将 cursor 的 键 设置为 found record 的键。
-
如果 cursor 的 键仅标记 为 false,则
-
将 cursor 的 获取值标记 设置为 true。
-
返回 cursor。
7. ECMAScript 绑定
本节定义本规范中定义的 键 值如何与 ECMAScript 值进行相互转换,以及如何使用 键路径 从 ECMAScript 值中提取或向其中注入这些键。本节引用了 ECMAScript 语言规范中的类型和算法并使用了一些算法约定。[ECMA-262] 此处未详述的转换定义在 [WEBIDL] 中。
7.1. 从值中提取键
要使用 value、keyPath 和可选的 multiEntry 标记 使用键路径从值中提取键,请运行以下步骤。这些步骤的结果是一个 键、无效、失败,或者步骤可能会抛出异常。
要使用 value 和 keyPath 在值上评估键路径,请运行以下步骤。这些步骤的结果是一个 ECMAScript 值或失败,或者步骤可能会抛出异常。
-
如果 keyPath 是一个字符串 列表,则
-
令 result 为一个如同通过表达式
[]创建的新的Array对象。 -
令 i 为 0。
-
对于 keyPath 中的每个 item
-
令 key 为递归 在值上评估键路径 且使用 item 和 value 的结果。
-
断言:key 不是一个 突然完成(abrupt completion)。
-
如果 key 为失败,中止整个算法并返回失败。
-
令 status 为 CreateDataProperty(result, p, key)。
-
断言:status 为 true。
-
将 i 增加 1。
-
-
返回 result。
注意: 这只会“递归”一层,因为 键路径 序列永远不能嵌套。
-
-
如果 keyPath 是空字符串,返回 value 并跳过剩余步骤。
-
令 identifiers 为对 keyPath 在 U+002E FULL STOP 字符 (.) 处进行 严格分割 的结果。
-
对于 identifiers 中的每个 identifier,跳转到下面适当的步骤
- 如果 Type(value) 是 String,且 identifier 为 "
length" -
令 value 为一个等于 value 中元素数量的 Number。
- 如果 value 是一个
Array且 identifier 为 "length" - 如果 value 是一个
Blob且 identifier 为 "size" -
令 value 为一个等于 value 的
size的 Number。 - 如果 value 是一个
Blob且 identifier 为 "type" -
令 value 为一个等于 value 的
type的 String。 - 如果 value 是一个
File且 identifier 为 "name" -
令 value 为一个等于 value 的
name的 String。 - 如果 value 是一个
File且 identifier 为 "lastModified" -
令 value 为一个等于 value 的
lastModified的 Number。 - 否则
-
-
如果 Type(value) 不为 Object,返回失败。
-
令 hop 为 ! HasOwnProperty(value, identifier)。
-
如果 hop 为 false,返回失败。
-
如果 value 为 undefined,返回失败。
-
- 如果 Type(value) 是 String,且 identifier 为 "
-
返回 value。
注意: 上述步骤中可以做出断言,因为该算法仅应用于 StructuredDeserialize 的输出值,且仅访问“自有(own)”属性。
7.2. 向值中注入键
注意: 本节中使用的 键路径 始终是字符串,绝不会是序列,因为不可能创建一个既有 键生成器 又有作为序列的 键路径 的 对象存储空间。
要使用 value 和 keyPath 检查键是否可以注入到值中,请运行以下步骤。这些步骤的结果为 true 或 false。
注意:上述步骤中可以进行断言,因为该算法仅应用于 StructuredDeserialize 的输出值。
对于 value、key 和 keyPath,执行以下步骤以使用键路径将键注入值中
-
令 identifiers 为在 U+002E FULL STOP 字符 (.) 处严格分割 keyPath 的结果。
-
断言:identifiers 不为空。
-
令 last 为 identifiers 的最后一项 item 并将其从列表中移除。
-
对于 identifiers 中的每个剩余 identifier
-
令 hop 为 ! HasOwnProperty(value, identifier)。
-
如果 hop 为 false,则
-
令 o 为通过表达式
({})创建的新Object。 -
令 status 为 CreateDataProperty(value, identifier, o)。
-
断言:status 为 true。
-
-
令 keyValue 为将键转换为值 key 的结果。
-
令 status 为 CreateDataProperty(value, last, keyValue)。
-
断言:status 为 true。
注意:上述步骤中可以进行断言,因为该算法仅应用于 StructuredDeserialize 的输出值,并且已经执行了检查键是否可以注入值中的步骤。
7.3. 将键转换为值
若要将键转换为值(给定 key),请执行以下步骤。这些步骤返回一个 ECMAScript 值。
-
令 type 为 key 的 类型 (type)。
-
令 value 为 key 的 值 (value)。
-
切换至 type
- number
-
返回等于 value 的 ECMAScript 数值。
- string
-
返回等于 value 的 ECMAScript 字符串值。
- 日期
-
-
令 date 为以单个参数 value 执行 ECMAScript Date 构造函数的结果。
-
断言:date 不是一个 异常完成 (abrupt completion)。
-
返回 date。
-
- 二进制:
- array
7.4. 将值转换为键
若要将值转换为键(给定 ECMAScript 值 input 和可选的 集合 (set) seen),请执行以下步骤。这些步骤的结果是一个 键,或者为“无效值 (invalid value)”、“无效类型 (invalid type)”,或者这些步骤可能会抛出异常。
-
如果未提供 seen,则令 seen 为一个新的空 集合。
-
如果 seen 包含 input,则返回“无效值”。
-
跳转到下方的相应步骤
- 如果 Type(input) 为 Number
- 如果 input 是一个
Date(具有 [[DateValue]] 内部槽位) - 如果 Type(input) 为 String
- 如果 input 属于 缓冲区源类型 (buffer source type)
-
-
如果 input 已分离 (detached),则返回“无效值”。
-
令 bytes 为执行获取缓冲区源 input 所持字节的副本的结果。
-
- 如果 input 是一个 数组奇异对象 (Array exotic object)
-
-
追加 input 到 seen。
-
令 keys 为一个新的空列表。
-
令 index 为 0。
-
当 index 小于 len 时
-
令 hop 为 ? HasOwnProperty(input, index)。
-
如果 hop 为 false,则返回“无效值”。
-
令 key 为将值转换为键(参数为 entry 和 seen)的结果。
-
ReturnIfAbrupt(key)。
-
如果 key 为“无效值”或“无效类型”,则中止这些步骤并返回“无效值”。
-
追加 key 到 keys。
-
将 index 增加 1。
-
-
返回一个新的 数组键 (array key),其 值 为 keys。
- 否则
-
返回“无效类型”。
若要将值转换为 multiEntry 键(给定 ECMAScript 值 input),请执行以下步骤。这些步骤的结果是一个 键,或者为“无效值”、“无效类型”,或者这些步骤可能会抛出异常。
-
如果 input 是一个 数组奇异对象,则
-
令 seen 为一个仅包含 input 的新 集合。
-
令 keys 为一个新的空 列表。
-
令 index 为 0。
-
当 index 小于 len 时
-
令 entry 为 Get(input, index)。
-
如果 entry 不是一个 异常完成,则
-
令 key 为将值转换为键(参数为 entry 和 seen)的结果。
-
如果 key 不是“无效值”、“无效类型”或一个 异常完成,且 keys 中没有等于 (equal to) key 的 item,则将 key 追加 (append) 到 keys。
-
-
将 index 增加 1。
-
-
否则,返回将值转换为键(参数为 input)的结果。如有异常则重新抛出。
注意:这些步骤类似于将值转换为键的步骤,但如果顶层值是 Array,则无法转换为键的成员将被忽略,且重复项会被移除。
例如,值 [10, 20, null, 30, 20] 会被转换为一个数组键,其 子键 (subkeys) 为 10, 20, 30。
8. 隐私考量
本节是非规范性的。
8.1. 用户跟踪
第三方主机(或任何能够将内容分发到多个站点的对象)可以使用存储在其客户端数据库中的唯一标识符来在多个会话中跟踪用户,并建立用户的活动档案。结合了解用户真实 ID 对象的站点(例如需要认证凭据的电子商务网站),这可能使压迫性团体能够比在纯匿名 Web 使用环境中更精确地定位个人。
有许多技术可以用来降低用户跟踪的风险:
- 拦截第三方存储
-
用户代理可以限制脚本对数据库对象的访问,仅允许源自顶层文档的浏览上下文域名的脚本进行访问,例如禁止运行在
iframe中的其他域名的页面访问该 API。 - 存储数据的过期
-
用户代理可以在一段时间后自动删除存储的数据。
这可以限制站点跟踪用户的能力,因为在这种情况下,站点仅能在用户与其自身进行认证(例如进行购买或登录服务)时,才能在多个会话中跟踪用户。
然而,这也使用户的数据处于风险之中。
- 将持久化存储视为 Cookie
-
用户代理应以一种将数据库功能与 HTTP 会话 Cookie 紧密关联的方式呈现给用户。[COOKIES]
这可能会鼓励用户以审慎的态度看待此类存储。
- 站点特定的数据库访问白名单
-
用户代理可能要求用户在站点使用该功能之前,先授权其访问数据库。
- 第三方存储的归属
-
用户代理可以记录包含导致数据存储的第三方来源 (origins) 内容的站点来源。
如果随后使用此信息来呈现当前持久化存储中数据的视图,它将允许用户对持久化存储的哪些部分进行清理做出明智的决定。结合黑名单(“删除此数据并禁止此域名再次存储数据”),用户可以将持久化存储限制在她信任的站点上。
- 共享黑名单
-
用户代理可能允许用户共享其持久化存储域名黑名单。
This would allow communities to act together to protect their privacy.
虽然这些建议防止了该 API 被用于琐碎的用户跟踪,但它们并没有完全阻止这种情况。在单一域名内,站点可以继续在会话期间跟踪用户,然后可以将所有这些信息与站点获取的任何识别信息(姓名、信用卡号、地址)一起传递给第三方。如果第三方与多个站点合作以获取此类信息,仍然可以创建用户档案。
However, user tracking is to some extent possible even with no cooperation from the user agent whatsoever, for instance by using session identifiers in URLs, a technique already commonly used for innocuous purposes but easily repurposed for user tracking (even retroactively). This information can then be shared with other sites, using visitors' IP addresses and other user-specific data (e.g. user-agent headers and configuration settings) to combine separate sessions into coherent user profiles.
8.2. Cookie 复活
如果持久化存储的用户界面将本规范中描述的持久化存储功能中的数据与 HTTP 会话 Cookie 中的数据分离开来,那么用户很可能会删除其中之一而不删除另一个。这将允许站点将这两个功能互为冗余备份,从而破坏用户保护其隐私的尝试。
8.3. 数据的敏感性
用户代理应将持久存储的数据视为潜在敏感数据;电子邮件、日历约会、健康记录或其他机密文档很有可能存储在此机制中。
To this end, user agents should ensure that when deleting data, it is promptly deleted from the underlying storage.
9. 安全考量
9.1. DNS 欺骗攻击
由于 DNS 欺骗攻击的潜力,无法保证声称在特定域名的主机确实来自该域名。为了缓解这种情况,页面可以使用 TLS。使用 TLS 的页面可以确信,只有同样使用 TLS 且拥有标识其为来自相同域名证书的页面才能访问其数据库。
9.2. 跨目录攻击
共享一个主机名的不同作者,例如在 geocities.com 上托管内容的用户,都共享同一组数据库。
没有通过路径名限制访问的功能。因此,建议共享主机上的作者避免使用这些功能,因为其他作者很容易读取数据并将其覆盖。
注意:即使提供了路径限制功能,通常的 DOM 脚本安全模型也会使得绕过此保护并从任何路径访问数据变得轻而易举。
9.3. 实现风险
实现这些持久化存储功能时,两个主要的风险是:允许恶意站点从其他域名读取信息,以及允许恶意站点写入稍后可被其他域名读取的信息。
让第三方站点读取不应从其域名读取的数据会导致信息泄露。例如,一个域名的用户购物愿望清单可能被另一个域名用于定向广告;或者一个文字处理站点存储的用户工作中机密文档可能被竞争公司的站点检查。
让第三方站点向其他域名的持久化存储写入数据可能导致信息欺骗,这也是同样危险的。例如,恶意站点可以向用户的愿望清单添加记录;或者恶意站点可以将用户的会话标识符设置为一个已知的 ID,恶意站点随后可以使用该 ID 在受害者站点上跟踪用户的操作。
因此,严格遵循本规范中描述的存储键分区模型对于用户安全至关重要。
如果主机名或数据库名被用于构建文件系统的持久化路径,则必须对其进行适当的转义,以防止攻击者使用诸如“../”之类的相对路径访问来自其他存储键的信息。
9.4. 持久化风险
实际的实现会将数据持久化到非易失性存储介质。数据在存储时将被序列化,并在检索时进行反序列化,尽管序列化格式的细节将取决于用户代理。用户代理可能会随着时间的推移更改其序列化格式。例如,格式可能会更新以处理新的数据类型,或提高性能。因此,为了满足本规范的操作要求,实现必须以某种方式处理较旧的序列化格式。对旧数据的处理不当可能导致安全问题。除了基本的序列化问题外,序列化数据还可能编码在用户代理的较新版本中不再有效的假设。
这方面的一个实际例子是 RegExp 类型。StructuredSerializeForStorage 操作允许序列化 RegExp 对象。典型的用户代理会将正则表达式编译为原生机器指令,并就如何传递输入数据和返回结果做出假设。如果此内部状态作为存储到数据库的数据的一部分被序列化,当内部表示稍后被反序列化时,可能会出现各种问题。例如,传递数据到代码的方式可能已经改变。编译器输出中的安全漏洞可能在用户代理的更新中已被识别并修复,但在序列化的内部状态中仍然存在。
用户代理必须识别并适当地处理旧数据。一种方法是在序列化格式中包含版本标识符,并在遇到旧数据时从脚本可见的状态重建任何内部状态。
10. 无障碍考量
本节是非规范性的。
本规范描述的 API 在无障碍方面的考虑有限:
-
它不提供内容的视觉呈现或对颜色的控制。
-
它不提供接受用户输入的功能。
-
它不提供用户交互功能。
-
它不定义文档语义。
-
它不提供基于时间的视觉媒体。
-
它不允许时间限制。
-
它不直接为最终用户提供文本、图形或其他非文本形式的内容。
-
它不定义传输协议。
该 API 确实允许存储结构化内容。文本内容可以作为字符串存储。API 中存在支持开发人员存储替代性非文本内容(如图像或音频)作为 Blob、File 或 ImageData 对象的功能。使用该 API 开发动态内容应用程序的开发人员应确保内容对于具有各种技术和需求的用户是可访问的。
虽然 API 本身没有定义具体的机制,但存储结构化内容也允许开发人员存储国际化内容,使用不同的记录或记录内的结构来容纳语言替代方案。
API 没有定义或要求用户代理生成用户界面来支持与 API 的交互。用户代理可以选择提供用户界面元素来支持 API。例如:当需要额外的存储配额时提示用户、观察特定网站使用的存储空间的功能,或者针对 API 存储的特定工具(如检查、修改或删除记录)。任何此类用户界面元素的设计都必须考虑到无障碍工具。例如,以图形形式呈现存储配额使用比例的用户界面,也必须将相同数据提供给屏幕阅读器等工具。
11. 修订历史
本节是非规范性的。
以下是自本规范上次发布以来更改的信息性摘要。完整的修订历史可以在此处找到。有关第一版的修订历史,请参阅该文档的修订历史。有关第二版的修订历史,请参阅该文档的修订历史。
-
清理 Indexed Database 事务算法现在返回一个值,以与其它规范集成。(PR #232)
-
更新了部分接口定义,因为
WindowOrWorkerGlobalScope现在是一个mixin。(PR #238) -
添加了
databases()方法。(issue #31) -
添加了
commit()方法。(issue #234) -
添加了
request属性。(issue #255) -
移除了对
File对象的非标准lastModifiedDate属性的处理。(issue #215) -
移除了
includes()方法的转义处理。(issue #294) -
将数组键限制为数组奇异对象(即禁止代理)。(issue #309)
-
事务在克隆操作期间现在被临时设为非活动状态。(PR #310)
-
添加了
durability选项和durability属性。(issue #50) -
更精确地指定了§ 2.7.2 事务调度,并禁止在具有重叠作用域的只读事务运行时启动读/写事务。(issue #253)
-
添加了无障碍考量部分。(issue #327)
-
使用了 [infra] 的列表排序定义。(issue #346)
-
添加了活跃 (live) 事务的定义,并将“运行升级事务”重命名为升级数据库,以消除“运行 (running)”的歧义。(issue #408)
-
指定了在 § 6.2 对象存储检索操作中从底层存储读取值失败时的
DOMException类型。(issue #423) -
更新了将值转换为键,使其对已分离的数组缓冲区返回无效。(issue #417)
-
更新了
open()以将其请求的已处理标志 (processed flag) 设置为 true。(issue #434) -
在
databases()中不包含尚未完成创建的数据库。(issue #442) -
澄清仅非活动 (inactive) 事务应尝试自动提交。(issue #436)
-
更正了升级数据库的步骤,以处理已中止的事务。(issue #436)
-
更新迭代游标的值序列化,对对象存储使用值而非引用值。(issue #452)
-
将源句柄 (source handle) 添加到游标,以避免向脚本暴露内部索引和对象存储。(issue #445)
-
定义了将数据库任务入队,并用其替换了将任务入队。(issue #421)
-
为
databases()添加了缺失的并行步骤。(issue #421) -
澄清了游标迭代谓词。(issue #450)
-
向
IDBObjectStore和IDBIndex添加了getAllRecords(options)方法。(issue #206) -
为
IDBObjectStore和IDBIndex的getAll()和getAllKeys()添加了 direction 选项。(issue #130) -
更新了对
QuotaExceededError的使用,以反映它现在是一个派生自DOMException的接口,而非异常名称。(issue #463) -
指定 null 对于错误 (error) 是有效的,并允许在中止事务中设置它。(issue #433)
-
在打开数据库连接中检查请求的错误,而不是检查升级事务。(issue #433)
-
移除了
abort()中冗余的事务状态更改。
12. 致谢
本节是非规范性的。
特别感谢第一版原作者 Nikunj Mehta,以及第一版的其他编辑 Jonas Sicking、Eliot Graff、Andrei Popescu 和 Jeremy Orlow。
Garret Swart 在本规范的设计中具有极大的影响力。
感谢 Tab Atkins, Jr. 创建并维护了 Bikeshed(用于创建本文件的规范编写工具),并感谢他提供的一般编写建议。
特别感谢 Abhishek Shanthkumar、Adam Klein、Addison Phillips、Adrienne Walker、Alec Flett、Andrea Marchesini、Andreas Butler、Andrew Sutherland、Anne van Kesteren、Anthony Ramine、Ari Chivukula、Arun Ranganathan、Ben Dilts、Ben Turner、Bevis Tseng、Boris Zbarsky、Brett Zamir、Chris Anderson、Dana Florescu、Danillo Paiva、David Grogan、Domenic Denicola、Dominique Hazael-Massieux、Evan Stade、Glenn Maynard、Hans Wennborg、Isiah Meadows、Israel Hilerio、Jake Archibald、Jake Drew、Jerome Hode、Josh Matthews、João Eiras、Kagami Sascha Rosylight、Kang-Hao Lu、Kris Zyp、Kristof Degrave、Kyaw Tun、Kyle Huey、Laxminarayan G Kamath A、Maciej Stachowiak、Marcos Cáceres、Margo Seltzer、Marijn Kruisselbrink、Ms2ger、Odin Omdal、Olli Pettay、Pablo Castro、Philip Jägenstedt、Shawn Wilsher、Simon Pieters、Steffen Larssen、Steve Becker、Tobie Langel、Victor Costan、Xiaoqian Wu、Yannic Bonenberger、Yaron Tausky、Yonathan Randolph 和 Zhiqiang Zhang,所有这些人的反馈和建议都促成了本规范的改进。