索引数据库 API 3.0

W3C 工作草案,

关于本文档的更多详情
此版本:
https://www.w3.org/TR/2025/WD-IndexedDB-3-20250813/
最新发布版本:
https://www.w3.org/TR/IndexedDB/
编辑草稿:
https://w3c.github.io/IndexedDB/
先前版本:
历史记录:
https://www.w3.org/standards/history/IndexedDB-3/
测试套件:
https://github.com/web-platform-tests/wpt/tree/master/IndexedDB
反馈:
GitHub
编辑:
(微软)
前编辑:
Ali Alabbas (前微软员工)
Joshua Bell (前谷歌员工)

摘要

本文档定义了用于保存简单值和层次对象记录的数据库 API。每条记录由一个键和一个值组成。此外,数据库会对其存储的记录维护索引。应用开发者可以直接使用 API,通过键或索引定位记录。可以在此 API 之上叠加查询语言。索引数据库可通过持久化 B-树数据结构实现。

本文档状态

本节描述了本文档在发布时的状态。当前 W3C 出版物列表及本技术报告的最新修订版可在 W3C 标准与草案索引中查阅。

本文档由 Web 应用工作组 按照 推荐流程工作草案身份发布。 作为工作草案发布并不代表 W3C 及其成员的认可。

本文档为草稿,可能随时更新、替换或被其他文档废止。 除作为正在进行的工作外,引用本文档为其他用途不合适。

本文档由遵循 W3C 专利政策的工作组制作。W3C 维护着公开专利披露清单,该页面还包含专利披露的说明。已知某专利包含必要声明的个人必须根据《W3C 专利政策》第6节披露相关信息。

本文档适用 2023年11月3日 W3C 流程文件

这是索引数据库 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-树数据结构实现,这在插入、删除以及对大量数据记录进行有序遍历时都非常高效。

下面的示例使用该 API 访问一个名为 "library" 的数据库。它有一个 "books" 对象存储区,按 "isbn" 属性作为主键存储图书记录。

图书记录有一个 "title" 属性。此示例人为要求书名必须唯一。代码通过创建名为 "by_title" 的索引,并设置 unique 选项来强制唯一性。该索引用于按标题查找图书,并防止添加重复书名。

图书记录还有一个 "author" 属性,并不要求唯一。代码创建了另一个名为 "by_author" 的索引,以便通过该属性查找。

代码首先打开数据库连接。upgradeneeded 事件处理代码在需要时创建对象存储区和索引。success 事件处理代码用于保存打开的连接,以便后续示例使用。

const request = indexedDB.open("library");
let db;

request.onupgradeneeded = function() {
  // 数据库之前不存在,因此创建对象存储区和索引。
  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");

  // 填充初始数据。
  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() {
  // 所有请求均已成功,事务已提交。
};

下面的示例通过标题使用索引查找单本书籍。

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) {
    // 找到匹配项。
    report(matching.isbn, matching.title, matching.author);
  } else {
    // 未找到匹配项。
    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) {
    // 针对每个匹配记录调用。
    report(cursor.value.isbn, cursor.value.title, cursor.value.author);
    cursor.continue();
  } else {
    // 没有更多匹配记录。
    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) {
  // "by_title" 索引的唯一性约束失败。
  report(request.error);
  // 可以调用 event.preventDefault() 以防止事务自动中止。
};
tx.onabort = function() {
  // 否则,事务会因请求失败自动中止。
  report(tx.error);
};

当数据库连接不再需要时,可以关闭。

db.close();

将来,数据库可能已扩展为包含其他对象存储区和索引。下面的示例展示了如何从旧版本迁移。

const request = indexedDB.open("library", 3); // 请求第 3 版。
let db;

request.onupgradeneeded = function(event) {
  const db = request.result;
  if (event.oldVersion < 1) {
    // 第 1 版为数据库的首个版本。
    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) {
    // 第 2 版新增了按年份索引图书。
    const bookStore = request.transaction.objectStore("books");
    const yearIndex = bookStore.createIndex("by_year", "year");
  }
  if (event.oldVersion < 3) {
    // 第 3 版新增了用于杂志的新对象存储区及两个索引。
    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 将会是 3。
};
单个数据库可被多个客户端(页面和 worker) 同时使用 —— 事务确保它们在读写时不会冲突。 如果有新客户端希望升级数据库(通过 upgradeneeded 事件),只有在所有其他客户端关闭了对当前版本数据库的连接后,才能进行升级。

为避免阻塞新客户端升级,客户端可以监听 versionchange 事件。当其他客户端希望升级数据库时会触发该事件。为允许升级继续进行,可以在收到 versionchange 事件时采取措施,最终关闭该客户端与数据库的连接

一种做法是重新加载页面:

db.onversionchange = function() {
  // 首先,保存所有未保存的数据:
  saveUnsavedData().then(function() {
    // 如果文档未被主动使用,可以在无需用户交互的情况下重新加载页面。
    if (!document.hasFocus()) {
      location.reload();
      // 页面重载将关闭数据库,也会加载新的 JavaScript 和数据库定义。
    } else {
      // 如果文档有焦点,强制重载页面可能会太扰乱用户。
      // 可以提示用户手动刷新:
      displayMessage("请刷新页面以获取最新版本。");
    }
  });
};

function saveUnsavedData() {
  // 如何保存取决于你的应用。
}

function displayMessage() {
  // 给用户展示非模态消息。
}

另一种方法是调用连接close() 方法。但需要确保你的应用对此有感知,因为后续访问数据库的尝试会失败。

db.onversionchange = function() {
  saveUnsavedData().then(function() {
    db.close();
    stopUsingTheDatabase();
  });
};

function stopUsingTheDatabase() {
  // 让应用进入不再使用数据库的状态。
}

新客户端(尝试升级的那个)可以通过 blocked 事件, 检测其他客户端是否阻止升级。blocked 事件在其他客户端的 versionchange 事件已触发但仍未断开连接时触发。

const request = indexedDB.open("library", 4); // 请求第 4 版。
let blockedTimeout;

request.onblocked = function() {
  // 给其他客户端异步保存数据的时间。
  blockedTimeout = setTimeout(function() {
    displayMessage("升级被阻止 - 请关闭其他正在显示本站的标签页。");
  }, 1000);
};

request.onupgradeneeded = function(event) {
  clearTimeout(blockedTimeout);
  hideMessage();
  // ...
};

function hideMessage() {
  // 隐藏之前显示的消息。
}

只有当其他客户端未能断开数据库连接时,用户才会看到上述消息。理想情况下,用户永远不会看到此消息。

2. 结构

名称是一个字符串,等同于DOMString; 即任意长度的 16 位码元序列,包括空字符串。名称始终作为不透明的 16 位码元序列进行比较。

注意: 因此,名称比较对大小写敏感,也对规范化形式、控制字符的包含或省略,以及 Unicode 文本的其它微小差异敏感。[Charmod-Norm]

如果实现使用的存储机制不支持任意字符串,则可采用转义机制或类似方法将提供的名称映射为可存储的字符串。

要从 列表names创建排序名称列表,执行以下步骤:

  1. sortednames升序排序,使用 码元小于算法。

  2. 返回一个与 sorted 关联的新DOMStringList

详情 这与 Array.prototype.sort() 方法对 Array 中的 String 的行为一致。 此排序按字符串的 16 位码元比较,保证高效、一致且确定性的顺序。结果列表不会匹配任何特定字母表或字典序,尤其是由代理对组成的码点。

2.1. 数据库

每个存储键都关联一组数据库数据库有零个或多个对象存储区,用于保存数据库中的数据。

一个数据库有一个名称,用于在特定存储键内标识它。名称为名称类型,并在数据库生命周期内保持不变。

一个数据库有一个版本。数据库首次创建时,版本 为 0(零)。

注意: 每个数据库任一时刻只有一个版本;数据库 不会同时存在多个版本。唯一更改版本的方法是使用升级事务

一个数据库最多关联一个升级事务,要么为 null,要么为升级事务,初始为 null。

2.1.1. 数据库连接

脚本不直接与数据库交互。脚本通过连接间接访问数据库。 连接对象可用于操作该数据库中的对象,也是获取该数据库 事务的唯一途径。

打开数据库会创建一个连接。 任一时刻,一个数据库可能有多个连接

连接只能访问与其打开所在全局作用域的存储键相关联的数据库

注意: 这不受Documentdomain更改影响。

连接有一个版本,在连接创建时设定。其值在连接 存续期内保持不变,除非升级被中止,此时设置为数据库的先前版本。一旦连接关闭,版本不会再变化。

每个连接有一个待关闭标志,初始为 false。

连接初建时为打开状态。连接可通过多种方式关闭。 若创建连接的执行上下文被销毁(如用户离开该页面),连接会关闭。也可使用关闭数据库连接步骤主动关闭。关闭后,待关闭标志会被设置为 true(若尚未设置)。

在异常情况下,如文件系统不可访问、权限变更或清除存储键的存储时,用户代理可关闭连接。此时必须用强制标志为 true 执行关闭数据库连接步骤。

连接有一个对象存储区集合,在创建连接时初始化为关联数据库中的对象存储区集合。除非有升级事务处于活动状态,集合内容保持不变。

连接get the parent算法返回 null。

若试图升级或删除数据库,会在打开的连接上触发类型为versionchange的事件。这样连接可选择关闭,以允许升级或删除继续进行。

若连接异常关闭,会在连接上触发类型为close的事件。

2.2. 对象存储区

对象存储区是数据库中数据的主要存储机制。

每个数据库有一组对象存储区。对象存储区的集合只能通过升级事务进行更改, 即响应upgradeneeded 事件。当新数据库创建时,不包含任何对象存储区

对象存储区有一个记录列表,用于保存对象存储区中的数据。每个记录由一个和一个组成。列表按键升序排序。对象存储区中不能有重复键的记录。

对象存储区有一个名称,是一个名称类型。 在所属数据库内,任一时刻名称都是唯一的。

对象存储区可选有键路径。有键路径时称为使用内联键;否则称为使用外部键

对象存储区可选有键生成器

对象存储可以从以下三种来源之一为记录派生

  1. 键生成器。键生成器每次需要键时生成单调递增的数字。

  2. 可通过键路径派生键。

  3. 存储值时可显式指定键。

2.2.1. 对象存储区句柄

脚本不直接与对象存储区交互。而是在事务中,脚本通过对象存储区句柄间接访问。

对象存储区句柄关联一个对象存储区 和一个事务。不同事务可关联同一个对象存储区的多个句柄, 但在同一事务内,某个对象存储区只能有一个对象存储区句柄

一个对象存储句柄有一个索引集,在创建对象存储句柄时,该索引集被初始化为引用关联对象存储索引集合。该集合的内容将保持不变,除非有升级事务处于激活状态

对象存储句柄拥有一个名称,在创建对象存储句柄时,该名称被初始化为关联对象存储名称。该名称将保持不变,除非有升级事务处于激活状态

2.3.

每条记录都关联一个。用户代理必须支持任意可序列化对象。这包括简单类型,如String 原始值和Date 对象,以及 ObjectArray 实例,File 对象、Blob 对象、ImageData 对象等。记录的是按值存储和检索的,而不是按引用;后续对值的修改不会影响数据库中已存储的记录。

记录的Records,由StructuredSerializeForStorage操作输出。

2.4.

为了高效检索存储在索引数据库中的记录,每条记录都按其组织。

有一个关联的类型,可为: number(数字), date(日期), string(字符串), binary(二进制), 或 array(数组)。

还关联一个, 其可能为: 当类型为numberdate时,为unrestricted double; 当类型为string时,为DOMString; 当类型为binary时,为字节序列; 当类型为array时,为其它组成的列表

ECMAScript [ECMA-262]值可按将值转换为键步骤转换为

注意: 以下 ECMAScript 类型是有效键:

尝试将其它 ECMAScript 值转换为 会失败。

数组键,其类型array子键数组键, 即数组键

比较两个键ab,执行如下步骤:

  1. taa类型

  2. tbb类型

  3. 如果ta不等于tb,则执行如下步骤:

    1. 如果taarray,则返回 1。

    2. 如果tbarray,则返回 -1。

    3. 如果tabinary,则返回 1。

    4. 如果tbbinary,则返回 -1。

    5. 如果tastring,则返回 1。

    6. 如果tbstring,则返回 -1。

    7. 如果tadate,则返回 1。

    8. 断言tbdate

    9. 返回 -1。

  4. vaa

  5. vbb

  6. 根据ta分情况:

    number
    date
    1. 如果va大于vb,则返回 1。

    2. 如果va小于vb,则返回 -1。

    3. 返回 0。

    string
    1. 如果va 码元小于vb,则返回 -1。

    2. 如果vb 码元小于va,则返回 1。

    3. 返回 0。

    binary
    1. 如果va 字节小于vb,则返回 -1。

    2. 如果vb 字节小于va,则返回 1。

    3. 返回 0。

    array
    1. lengthva长度vb长度的较小值。

    2. i为 0。

    3. i小于length时,执行:

      1. c为递归比较两个键,参数为va[i]和 vb[i]。

      2. 如果c不为 0,则返回c

      3. i加 1。

    4. 如果va长度大于vb长度,则返回 1。

    5. 如果va长度小于vb长度,则返回 -1。

    6. 返回 0。

a大于 b,若用比较两个键步骤比较ab结果为 1。

a小于 b,若用比较两个键步骤比较ab结果为 -1。

a等于 b,若用比较两个键步骤比较ab结果为 0。

注意: 按上述规则,负无穷是的最小可能值。 number键小于date键。 date键小于string键。 string键小于binary键。 binary键小于array键。 没有最大可能值。 因为任何候选最大的数组,再加一个键,会更大。

注意: binary键的成员按无符号字节值(0 到 255)比较, 而不是按带符号byte 值(-128 到 127)。

2.5. 键路径

键路径是一个字符串或字符串列表, 用于定义如何从中提取有效的键路径包括:

注意: 键路径中不允许有空格。

键路径值只能通过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

在上例中,索引中键为"Alice"、值为123的记录,其被引用值{ name: "Alice", title: "CEO" }

注意: 索引中的每条记录只引用索引的被引用对象存储区的一条记录。 但一个索引中可以有多条记录引用对象存储区的同一条记录,也可能没有任何记录引用对象存储区的某条记录。

索引中的记录总是按记录的键排序。 与对象存储区不同,一个索引可包含多个键相同的记录。这些记录会再根据索引记录的值 (即被引用对象存储区记录的键)排序。

索引有一个名称,为名称类型。 在索引的被引用对象存储区内,名称始终唯一。

索引唯一性标志。为 true 时,索引会强制索引中没有两条记录具有相同的键。如果尝试在索引引用的对象存储区插入或修改某条记录, 使得索引的键路径计算结果已在索引中存在,则对象存储区的插入或修改操作会失败。

索引有一个multiEntry 标志。该标志会影响当索引的键路径的求值结果为数组键时的行为。如果multiEntry 标志为 false,则会向索引中添加一个记录,其数组键。如果multiEntry 标志为 true,则会为每个子键向索引中添加一个记录

2.6.1. 索引句柄

脚本不直接与索引交互。而是在事务中, 通过索引句柄间接访问索引。

索引句柄关联一个索引和一个 对象存储区句柄索引句柄事务 即其关联对象存储区句柄的事务。不同事务可关联同一个索引的多个句柄, 但在同一事务内,某个索引只能有一个索引句柄

索引句柄有一个名称,在创建该索引句柄时初始化为其关联索引名称。只有在升级事务处于活动状态时名称才会变化。

2.7. 事务

事务用于与数据库中的数据交互。所有数据的读写都必须通过事务完成。

事务可为应用和系统故障提供保护。事务可用于存储多条数据记录或有条件地修改某些数据记录。事务表示一组原子且持久的数据访问和数据变更操作。

所有事务都通过一个连接创建,该连接是事务的连接

事务有一个作用域,即该事务可操作的对象存储区集合

注意:事务的作用域保持不变,除非该事务是升级事务

若两个事务作用域有任意对象存储区重叠,则称这两个事务作用域重叠(overlapping scope)。

事务有一个模式,决定该事务可执行的操作类型。模式在事务创建时设定,并在事务生命周期内保持不变。事务的模式可为:

"readonly"

只允许读取数据,不能进行修改。优点是多个只读事务可同时启动, 即使他们的作用域有重叠(使用相同对象存储区)。只要数据库已打开,随时可创建此类事务。

"readwrite"

允许读取、修改和删除现有对象存储区的数据,但不能添加或移除对象存储区和索引。若多个"readwrite" 事务的作用域 有重叠,则不能同时启动,因为可能会在事务进行期间互相修改数据。只要数据库已打开,随时可创建此类事务。

"versionchange"

允许读取、修改和删除现有对象存储区的数据,也可以创建和移除对象存储区及索引。只有此类事务可以进行这些操作。该事务不能手动创建,而是在触发 upgradeneeded 事件时自动创建。

事务有一个持久化提示。此提示用于告知用户代理在提交事务时应优先考虑性能还是持久性。持久化提示可为:

"strict"

只有在所有待提交更改成功写入持久存储介质后,用户代理才可认为事务已成功提交

"relaxed"

只要所有待提交更改已写入操作系统,无需后续验证,用户代理即可认为事务已成功提交

"default"

用户代理应为存储桶使用其默认持久化行为。若未指定,事务默认使用此模式。

注意: 在典型实现中,"strict" 是对用户代理的提示,在触发complete 事件前应刷新操作系统 I/O 缓冲区。这样可提高数据在操作系统崩溃或断电情况下持久保存的概率,但刷新缓冲区可能耗时且消耗便携设备的电池寿命。

建议 Web 应用对临时数据(如缓存或快速变化记录)使用"relaxed", 对于需降低数据丢失风险的场景使用"strict"。 实现应根据应用的持久化提示权衡对用户和设备的影响。

事务可选有一个清理事件循环, 即事件循环

事务有一个请求列表,即该事务下所有待处理的请求

一个事务 有一个错误,当事务中止时设置。

注意: 实现者需要注意,值 "null" 也被视为错误,因为它是通过 abort() 设置的。

事务get the parent 算法返回事务的连接

只读事务事务, 模式为"readonly"。

读写事务事务, 模式为"readwrite"。

2.7.1. 事务生命周期

事务有一个状态,可为以下几种:

活动

事务初次创建时,以及在分发与该事务关联的请求事件期间,处于此状态。

当事务处于此状态时,可以发起新的请求

非活动

事务创建后,控制返回事件循环或事件未分发时,处于此状态。

当事务处于此状态时,不能发起请求

提交中

当事务关联的所有请求完成后,事务进入此状态,尝试提交

当事务处于此状态时,不能发起请求

已结束

事务成功提交或中止后,进入此状态。

当事务处于此状态时,不能发起请求

事务应短暂存续。自动提交机制(见下文)鼓励这一点。

注意: 作者仍可让事务长时间存活; 但这种用法不建议,因为会影响用户体验。

生命周期如下:

  1. 事务在创建时,指定作用域模式。初始状态为活动

  2. 当实现能够强制事务的作用域模式约束(见下文),应排队数据库任务,异步启动事务。

    事务启动后,实施可开始执行针对该事务发起的请求。请求必须按发起顺序依次执行,结果也要按请求顺序返回。不同事务的请求结果返回顺序不保证。

    注意: 事务模式确保不同事务的请求可任意顺序执行,且不会影响数据库存储的数据。

  3. 当事务的每个请求处理后,会触发successerror 事件。事件分发期间,事务状态设为活动,允许新增请求。事件分发完成后,事务状态设回非活动

  4. 事务可在结束前任意时刻中止, 即使未处于活动或尚未启动

    显式调用abort() 会发起中止。脚本未处理的请求失败也会导致中止。

    中止事务时,实施必须回滚事务期间对数据库做的所有变更,包括对象存储区内容及对象存储区、索引的添加与移除。

  5. 当事务所有请求完成且结果已处理,且未被中止,实施应尝试提交一个非活动事务。

    显式调用commit() 可立即发起提交,无需等待请求结果由脚本处理。

    提交时,事务状态设为提交中。实施必须原子性地写入事务请求产生的所有数据库变更。要么全部写入,要么遇到错误(如磁盘写入失败)则不写入任何变更,并按中止事务步骤处理。

  6. 事务成功提交中止后,状态设为已结束

实施必须允许在事务处于活动状态时发起请求。即使事务尚未启动。在事务启动前,实施不得执行这些请求,但必须记录请求及其顺序。

事务创建起,直到状态设为已结束,期间被视为存活

清理索引数据库事务,执行如下步骤。如果有事务被清理则返回 true,否则返回 false。

  1. 如果没有事务清理事件循环与当前事件循环一致,返回 false。

  2. 对每个事务transaction,其清理事件循环与当前事件循环一致:

    1. transaction状态非活动

    2. 清除transaction清理事件循环

  3. 返回 true。

注意: 这些步骤由[HTML]调用。确保通过脚本调用transaction()创建的事务在任务完成后被停用。每个事务最多运行一次这些步骤。

类型为complete的事件会在成功提交事务上触发。

类型为abort的事件会在中止事务上触发。

2.7.2. 事务调度

以下约束定义了何时可以启动事务

实现可能还会施加额外约束。例如,实现无需并行启动作用域重叠读写事务,或可能对已启动的事务数量设限。

注意: 这些约束意味着:
  • 允许任意数量的只读事务并发启动,即使它们有作用域重叠

  • 只要只读事务存活, 实现通过该事务创建的请求返回的数据保持不变。即,两次读取同一数据的请求,无论数据是否存在,结果都一样。

  • 读写事务只受自身对对象存储区的更改影响。实现确保其他事务不会修改该读写事务作用域内的对象存储区内容。同时,保证若该读写事务成功完成,其变更可无冲突地提交到数据库

  • 若多个读写事务尝试访问同一个对象存储区(即作用域重叠),最先创建的事务优先获得访问权,且在事务结束前,只有它能访问该对象存储区。

  • 在某个读写事务之后创建的任意事务都能看到该读写事务写入的变更。例如,读写事务A创建后,随后又创建事务B,且两者有作用域重叠,则事务B可以看到A在重叠作用域内对象存储区所做的任何更改。但直到A结束前,B无法访问这些对象存储区。

2.7.3. 升级事务

升级事务事务,其模式 为"versionchange"。

当打开连接数据库,指定的版本大于当前版本时,运行升级数据库步骤会自动创建升级事务。该事务会在upgradeneeded 事件处理器内处于活动状态。

注意: 升级事务允许在数据库内创建、重命名和删除对象存储区索引

升级事务是排他性的。打开数据库连接步骤确保升级事务存活期间,数据库只有一个连接处于打开状态。 只有在所有其他连接关闭后,upgradeneeded 事件才会被触发,升级事务才会启动。这样可确保所有之前的事务均已结束

只要升级事务存活,对同一数据库打开更多连接的尝试会被延迟,且对同一连接调用transaction() 启动新事务会抛出异常。这样可以确保没有其他事务同时存活,也确保在升级事务存活期间不会对同一数据库排队新事务。

此外还保证一旦升级事务完成,数据库中的对象存储区索引集合在所有后续连接事务生命周期内保持不变。

2.8. 请求

数据库的每个异步操作都通过请求完成。每个请求代表一个操作。

请求有一个已处理标志,初始值为 false。当与请求关联的操作被执行后,该标志会被设置为 true。

当请求的已处理标志为 true 时,请求被称为已处理

请求有一个完成标志,初始值为 false。当与请求关联的操作结果可用时,该标志会被设置为 true。

请求有一个对象。

请求有一个结果和一个错误,在完成标志为 true 之前都无法访问。

请求有一个事务,初始值为 null。当请求根据异步执行请求步骤被放置到某个事务时会被设置。

当一个请求被发起时,会返回一个新的请求,其完成标志为 false。如果请求成功完成,其完成标志会被设置为 true,结果会被设置为请求的结果,并且会在请求上触发类型为success的事件。

如果在执行操作时发生错误,请求的完成标志会被设置为 true,错误会被设置为该错误,并且会在请求上触发类型为error的事件。

请求获取父对象算法返回请求的事务

注意: 请求通常不会被重复使用,但也有例外。例如,当一个游标被迭代时,迭代结果会在用于打开游标的同一个请求对象上报告。当需要升级事务时,打开请求会同时用于upgradeneeded事件和最终打开操作的结果。在某些情况下,请求的完成标志会先被设置为 false,再被设置为 true,且结果可能会发生变化或者错误会被设置。

2.8.1. 打开请求

打开请求是一种特殊类型的请求,用于打开连接或删除数据库。除了successerror事件外,还可能会在打开请求上触发blockedupgradeneeded事件以指示进度。

打开请求始终为 null。

打开请求事务为 null,除非已触发upgradeneeded事件。

打开请求获取父对象算法返回 null。

2.8.2. 连接队列

打开请求会在连接队列中处理。 队列包含所有与某个存储键名称关联的打开请求。队列中的请求按顺序处理,每个请求必须运行至完成,才能处理下一个请求。打开请求可能因其他连接阻塞,需要这些连接关闭后,请求才能完成并允许后续请求处理。

注意: 连接队列不是与任务队列相关联的事件循环,因为请求是在任何特定浏览上下文之外处理的。对于已完成的打开请求,事件的派发仍然会通过与请求发起时所在上下文的事件循环相关联的任务队列进行。

2.9. 键区间

可以通过键区间对象存储区索引检索记录。键区间是用于键的数据类型上的连续区间。

键区间有一个下界(null 或)。

键区间有一个上界(null 或)。

键区间有一个下界开区标志。除非另有说明,默认为 false。

键区间有一个上界开区标志。除非另有说明,默认为 false。

键区间下界可以与上界相等键区间不允许下界大于上界

键区间仅包含key,即下界与上界都等于key

当满足以下两个条件时,key在键区间内range

注意:

无界键范围键范围,其下界上界都为 null。所有属于无界键范围

将值转换为键区间,参数为value和可选null disallowed flag,执行如下步骤:

  1. 如果value键区间,返回value

  2. 如果value为 undefined 或 null,若null disallowed flag为 true,则抛出"DataError" DOMException,否则返回无界键区间

  3. key为用value转换为键的结果。任何异常均重新抛出。

  4. key为"invalid value"或"invalid type",抛出"DataError" DOMException

  5. 返回仅包含key键区间

潜在有效键区间是可转换为键区间类型的 ECMAScript 值。 至于具体值能否成功转换为键区间(即用其将值转换为键区间会不会抛异常),不做要求。

注意: 例如,分离的 BufferSource潜在有效键区间,但用将值转换为键区间时会抛异常。

要判断 ECMAScript value是否为潜在有效键区间,执行如下步骤:

  1. 如果value键区间,返回 true。

  2. key为用value转换为键的结果。

  3. key为"invalid type",返回 false。

  4. 否则返回 true。

注意: getAll()getAllKeys() 方法用是否为潜在有效键区间处理第一个参数。 若参数为潜在有效键区间,则getAll()getAllKeys() 用该参数执行将值转换为键区间。否则使用IDBGetAllOptions作为第一个参数。

getAll()getAllKeys() 对第一个参数为DateArrayArrayBuffer ,且用转换为键返回"invalid value"时会抛异常。 例如,用 NaN Date作为第一个参数调用getAll() 会抛异常,而不是用默认值执行IDBGetAllOptions字典。

2.10. 游标

游标用于以特定方向遍历索引对象存储区中的一组记录。

游标有一个源句柄,即打开该游标的索引句柄对象存储区句柄

游标有一个事务, 即游标源句柄的事务

游标有一个记录区间,存在于索引或对象存储区中。

游标有一个,它是游标的源句柄中的一个索引对象存储。 游标的表示游标正在遍历的记录所属的索引对象存储。 如果游标的源句柄索引句柄,那么游标的就是该索引句柄关联的索引。 否则,游标的就是该对象存储句柄关联的对象存储

游标有一个方向,决定游标遍历记录时是按键单调递增还是递减,并决定遍历索引时是否跳过重复值。方向也决定游标初始位置在源的起点还是终点。游标的方向可为:

"next"

该方向使游标在源的起点打开。遍历时游标应以键值单调递增顺序返回所有记录(包括重复项)。

"nextunique"

该方向使游标在源的起点打开。遍历时游标不返回键值相同的记录,只返回每个键的第一条记录,其余记录按单调递增顺序返回。当源为对象存储区或唯一性标志为 true 的索引时,此方向行为与 "next" 完全一致。

"prev"

该方向使游标在源的终点打开。遍历时游标应以键值单调递减顺序返回所有记录(包括重复项)。

"prevunique"

该方向使游标在源的终点打开。遍历时游标不返回键值相同的记录,只返回每个键的第一条记录,其余记录按单调递减顺序返回。当源为对象存储区或唯一性标志为 true 的索引时,此方向行为与 "prev" 完全一致。

游标在其范围内有一个位置。游标正在遍历的记录列表在整个范围迭代完成前可能会发生变化。为了解决这个问题,游标并不是以索引来维护其位置,而是以上一次返回的记录的来维护。对于正向遍历的游标,下次迭代时会返回键值比上一次返回的记录更大的最小记录。对于反向遍历的游标,则相反,会返回键值比上一次返回的记录更小的最大记录。

对于遍历索引的游标情况会稍复杂一些,因为多个记录可能具有相同的键,因此还会按进行排序。遍历索引时,游标还拥有一个对象存储位置,用于表示索引中先前找到的记录。寻找下一个合适的记录时,会同时使用位置对象存储位置

游标有一个和一个,分别表示上一次迭代的记录

游标有一个获取值标志。当该标志为 false 时,游标要么正在加载下一个值,要么已经到达其范围末尾。当它为 true 时,表示游标当前持有一个值,并且准备好迭代到下一个值。

如果游标的对象存储,那么游标的有效对象存储就是该对象存储,游标的有效键就是游标的位置。如果游标的索引,那么游标的有效对象存储就是该索引的被引用对象存储有效键是游标的对象存储位置

游标有一个请求,即用于打开游标的请求

游标还有一个仅键标志,用于指示游标的是否通过 API 公开。

2.11. 键生成器

创建对象存储区时,可指定使用键生成器。键生成器用于为插入到对象存储区的记录生成键(若未显式指定)。

键生成器有一个当前数值当前数值始终为小于等于 253 (9007199254740992) + 1 的正整数。键生成器的当前数值初始为 1,在关联对象存储区创建时设定。当前数值随生成键递增,也可通过显式键更新为特定值。

注意: 每个使用键生成器的对象存储区都有独立的生成器。操作某对象存储区不会影响其他对象存储区的键生成器。

修改键生成器的当前数值属于数据库操作。如果操作失败并被回滚,当前数值会恢复为操作前的值。无论是因键生成器使用而递增,还是因存储记录时使用显式键而修改,均适用此规则。

同样,如事务中止,事务作用域内每个对象存储区的键生成器当前数值会恢复为事务开始前的值。

键生成器的当前数值不会减少,除非数据库操作被回滚。删除记录不会影响对象存储区的键生成器。即使清空所有记录(如调用clear()方法),也不会影响键生成器的当前数值

当存储记录且未指定时,将自动生成键。

要为对象存储区store生成键,执行:

  1. generatorstore键生成器

  2. keygenerator当前数值

  3. key大于 253 (9007199254740992),返回失败。

  4. generator当前数值加 1。

  5. 返回key

当存储记录且指定时,关联的键生成器可能被更新。

要为对象存储区store,用key可能更新键生成器,执行:

  1. key类型不是number,终止步骤。

  2. valuekey

  3. valuevalue与 253 (9007199254740992) 的较小值。

  4. value为不大于value的最大整数。

  5. generatorstore键生成器

  6. value大于等于generator当前数值,则设generator当前数值value加 1。

注意: 对于使用内联键的对象存储,可以通过设置存储值中对象存储的键路径指向的属性来指定键; 对于使用外部键的对象存储,则可以在存储记录时传递一个键参数来指定键。

只有类型number的指定键才会影响键生成器的当前数值。类型为datearray(无论包含什么键)、binarystring(无论是否能被解析为数字)的键都不会影响键生成器的当前数值。类型为number小于 1 的键不会影响当前数值,因为它们始终小于键生成器的当前数值

当键生成器的当前数值超过 253 (9007199254740992) 时,任何后续使用键生成器生成新的尝试都会导致抛出"ConstraintError" DOMException。但仍可通过显式指定键插入记录,要重新使用键生成器只能删除并新建对象存储区。

注意: 此限制是因为 ECMAScript Number无法唯一表示大于 9007199254740992 的整数。例如,9007199254740992 + 1 === 9007199254740992在 ECMAScript 中成立。

只要正常使用键生成器,这个限制不会成为问题。如果你每天每秒生成 1000 个新键,也要 285000 年才会遇到上限。

实际结果是,对象存储区第一个生成的键总是 1(除非先插入更大的数值键),且生成的键总是比存储区中最大数值键大。除非事务回滚,同一个对象存储区不会生成重复键。

每个对象存储区都有自己的键生成器:

store1 = db.createObjectStore("store1", { autoIncrement: true });
store1.put("a"); // 得到键 1
store2 = db.createObjectStore("store2", { autoIncrement: true });
store2.put("a"); // 得到键 1
store1.put("b"); // 得到键 2
store2.put("b"); // 得到键 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"}); // 得到键 1
store.put({ ix: "a"}); // 失败
store.put({ ix: "b"}); // 得到键 2

移除对象存储区的项不会影响键生成器,即使调用clear()也不影响。

store = db.createObjectStore("store1", { autoIncrement: true });
store.put("a"); // 得到键 1
store.delete(1);
store.put("b"); // 得到键 2
store.clear();
store.put("c"); // 得到键 3
store.delete(IDBKeyRange.lowerBound(0));
store.put("d"); // 得到键 4

插入显式键仅当键为数值且高于最后一次生成的键时才影响键生成器。

store = db.createObjectStore("store1", { autoIncrement: true });
store.put("a"); // 得到键 1
store.put("b", 3); // 使用键 3
store.put("c"); // 得到键 4
store.put("d", -10); // 使用键 -10
store.put("e"); // 得到键 5
store.put("f", 6.00001); // 使用键 6.0001
store.put("g"); // 得到键 7
store.put("f", 8.9999); // 使用键 8.9999
store.put("g"); // 得到键 9
store.put("h", "foo"); // 使用键 "foo"
store.put("i"); // 得到键 10
store.put("j", [1000]); // 使用键 [1000]
store.put("k"); // 得到键 11
// 若对象存储区使用 keyPath 且显式键为内联,行为一致。

中止事务会回滚事务期间对键生成器的所有递增。这保证所有回滚一致,因为因崩溃导致的回滚无法提交递增后的键生成器值。

db.createObjectStore("store", { autoIncrement: true });
trans1 = db.transaction(["store"], "readwrite");
store_t1 = trans1.objectStore("store");
store_t1.put("a"); // 得到键 1
store_t1.put("b"); // 得到键 2
trans1.abort();
trans2 = db.transaction(["store"], "readwrite");
store_t2 = trans2.objectStore("store");
store_t2.put("c"); // 得到键 1
store_t2.put("d"); // 得到键 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: {} }。保存到对象存储后,foobarbaz属性会依次作为父子关系被创建,直到可以为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 将会是: { zip: {}, foo: { bar: { baz: 1 } } }
    console.assert(value.foo.bar.baz === 1);
  };
};

试图在原始值上存储属性会失败并抛出错误。下方第一个示例中,对象存储的键路径为"foo"。实际对象是值为4的原始类型。在该原始值上定义属性会失败。

const store = db.createObjectStore("store", { keyPath: "foo", autoIncrement: true });

// 键生成会尝试在该原始值上创建和存储键路径属性
// property on this primitive.
store.put(4); // 会抛出 DataError

2.12. 记录快照

记录快照 包含从对象存储区记录索引记录复制的键和值。

记录快照有一个,类型为

记录快照有一个,类型为

注意: 对于索引记录,快照的是记录的被引用值的副本。 对于对象存储区记录,快照的即为记录的值

记录快照还有一个主键,类型为

注意: 对于索引记录,快照的主键为记录的,即索引中该记录在被引用对象存储区的键。 对于对象存储区记录,快照的主键是同一个,即记录的

3. 异常

本文件中使用的异常均为DOMExceptionDOMException派生接口,详见[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 实例的属性获取信息。

这些任务的 任务源数据库访问任务源

排队数据库任务,在 queue a task 上对 数据库访问任务源进行操作。

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

返回请求针对的 IDBObjectStoreIDBIndexIDBCursor,若为 打开请求则为 null。

request . transaction

返回请求所在的 IDBTransaction。若为 打开请求,则在 升级事务存活期间返回升级事务,否则为 null。

request . readyState

在请求未完成前,返回 "pending",完成后返回 "done"。

result 的 getter 步骤如下:
  1. 如果 this完成标志为 false,则 抛出 "InvalidStateError" DOMException

  2. 返回 this结果,若请求有错误则返回 undefined。

error 的 getter 步骤如下:
  1. 如果 this完成标志为 false,则 抛出 "InvalidStateError" DOMException

  2. 返回 this错误,若无错误则为 null。

source 的 getter 步骤为返回 this,若未设置则为 null。

transaction 的 getter 步骤为返回 this事务

注意: transaction getter 在某些请求(如 open() 返回的请求)会返回 null。

readyState 的 getter 步骤为:若 this完成标志为 false,则返回 "pending",否则返回 "done"。

onsuccess 属性是 事件处理 IDL 属性,其 事件类型success

onerror 属性是 事件处理 IDL 属性,其 事件类型error 事件。

IDBDatabase 上返回 打开请求的方法会使用扩展接口,以便监听 blockedupgradeneeded 事件。

[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 oldVersion = 0;
  unsigned long long? newVersion = null;
};

oldVersion 的 getter 步骤为返回其初始化值,表示数据库的前一个版本。

newVersion 的 getter 步骤为返回其初始化值,表示数据库的新版本,若数据库正在被删除则为 null。参见升级数据库步骤。

事件按 DOM § 2.5 构造事件定义构建。

触发版本变更事件,名称为 e,目标为 target,参数为 oldVersionnewVersion,执行如下步骤:

  1. event 为使用 创建事件得到的 IDBVersionChangeEvent

  2. eventtype 属性为 e

  3. eventbubblescancelable 属性为 false。

  4. eventoldVersion 属性为 oldVersion

  5. eventnewVersion 属性为 newVersion

  6. legacyOutputDidListenersThrowFlag 为 false。

  7. target 上分发 event,带参数 legacyOutputDidListenersThrowFlag

  8. 返回 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)

尝试打开名为 数据库连接,版本为当前版本,不存在则为 1。请求成功时,requestresult连接

request = indexedDB . open(name, version)

尝试打开名为 数据库连接,版本为指定 version。若数据库存在且版本较低,且有未响应 versionchange 事件而未关闭的连接,请求会被阻塞,直到所有连接关闭,然后进行升级。若数据库存在且版本较高,请求会失败。请求成功时,requestresult连接

request = indexedDB . deleteDatabase(name)

尝试删除名为 数据库 的数据库。若数据库存在且有未响应 versionchange 事件而未关闭的连接,请求会被阻塞直到所有连接关闭。请求成功时,requestresult 为 null。

result = await indexedDB . databases()

返回一个 Promise,解析为给定 存储键 下的数据库名称和版本快照列表。

此 API 用于 Web 应用自省数据库使用(如清理旧版本站点数据)。注意,结果只是快照;无法保证数据收集与响应发送的时序与创建、升级或删除数据库请求的顺序一致(包括本上下文及其它上下文)。

open(name, version) 方法步骤如下:

  1. version 为 0,抛出 TypeError

  2. environmentthis相关设置对象

  3. storageKey 为以 environment 运行 获取存储键的结果。若失败,则 抛出 "SecurityError" DOMException 并终止步骤。

  4. request 为新的 打开请求

  5. 以下步骤并行执行:

    1. result打开数据库连接的结果,参数为 storageKeyname、(如有)version,否则为 undefined,以及 request

      若未指定 version 会怎样? 若未指定 version 且已存在同名 数据库,则直接打开连接且不改变 版本。若不存在同名数据库,则新建数据库版本为 1。
    2. request已处理标志为 true。

    3. 排队数据库任务以执行如下步骤:

      1. result 为错误:

        1. request结果为 undefined。

        2. request错误result

        3. request完成标志为 true。

        4. 触发事件 errorrequest,其 bubblescancelable 属性初始为 true。

      2. 否则:

        1. request结果result

        2. request完成标志为 true。

        3. 触发事件 successrequest

        注意: 若上述步骤导致运行了升级事务,则这些步骤会在事务结束后执行。这样可保证如果即将发生版本升级,成功事件会先在连接上触发,使脚本有机会注册 versionchange 事件监听器。

        为什么没有用触发成功事件触发错误事件的步骤? 此时请求没有关联事务,所以这些步骤——在分发前激活事务、分发后停用事务——不适用。
  6. 返回新的 IDBOpenDBRequest 对象,关联 request

deleteDatabase(name) 方法步骤如下:

  1. environmentthis相关设置对象

  2. storageKey 为以 environment 运行 获取存储键的结果。若失败,则 抛出 "SecurityError" DOMException 并终止步骤。

  3. request 为新的 打开请求

  4. 以下步骤并行执行:

    1. result删除数据库的结果,参数为 storageKeynamerequest

    2. request已处理标志为 true。

    3. 排队数据库任务以执行如下步骤:

      1. result 为错误,设 request错误result完成标志为 true, 并 触发事件 errorrequest,其 bubblescancelable 属性初始为 true。

      2. 否则,设 request结果为 undefined, 完成标志为 true, 并 触发版本变更事件 名为 successrequest,参数为 result 和 null。

        为什么没有用触发成功事件触发错误事件的步骤? 该请求没有关联事务,所以这些步骤(分发前激活事务、分发后停用事务)不适用。

        此外,此处的 success 事件为 IDBVersionChangeEvent,包含 oldVersionnewVersion 信息。

  5. 返回新的 IDBOpenDBRequest 对象,关联 request

databases() 方法步骤如下:

  1. environmentthis相关设置对象

  2. storageKey 为以 environment 运行 获取存储键的结果。若失败,则返回 一个被拒绝的 promise,原因为 "SecurityError" DOMException

  3. p新 promise

  4. 以下步骤并行执行:

    1. databases集合storageKey 下的 数据库。若无法确定,则 排队数据库任务拒绝 p,原因为合适错误(如 "UnknownError" DOMException),并终止步骤。

    2. result 为新 列表

    3. 遍历 databases 中每个 db

      1. db版本为 0,则 跳过

      2. info 为新 IDBDatabaseInfo 字典。

      3. infoname 字典成员为 db名称

      4. infoversion 字典成员为 db版本

      5. 追加 inforesult

    4. 排队数据库任务解析 p,值为 result

  5. 返回 p

🚧 databases() 方法是本版本新增。 支持 Chrome 71, Edge 79, Firefox 126, Safari 14。 🚧
result = indexedDB . cmp(key1, key2)

比较两个值作为 。若 key1key2 之前返回 -1,key2key1 之前返回 1,相等返回 0。

若任一输入不是有效 ,抛出 "DataError" DOMException

cmp(first, second) 方法步骤如下:

  1. a 为用 first 转换为键的结果。异常重新抛出。

  2. a 为 "invalid value" 或 "invalid type",抛出 "DataError" DOMException

  3. b 为用 second 转换为键的结果。异常重新抛出。

  4. b 为 "invalid value" 或 "invalid type",抛出 "DataError" DOMException

  5. 返回用 ab 比较两个键 的结果。

4.4. IDBDatabase 接口

IDBDatabase 接口表示与 数据库连接

当关联的 连接关闭待定标志为 false 且注册了类型为 aborterrorversionchange 的一个或多个事件监听器时,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 mode = "readonly",
                                         optional IDBTransactionOptions options = {});
  undefined close();

  [NewObject] IDBObjectStore createObjectStore(
    DOMString name,
    optional IDBObjectStoreParameters options = {});
  undefined deleteObjectStore(DOMString name);

  // Event handlers:
  attribute EventHandler onabort;
  attribute EventHandler onclose;
  attribute EventHandler onerror;
  attribute EventHandler onversionchange;
};

enum IDBTransactionDurability { "default", "strict", "relaxed" };

dictionary IDBTransactionOptions {
  IDBTransactionDurability durability = "default";
};

dictionary IDBObjectStoreParameters {
  (DOMString or sequence<DOMString>)? keyPath = null;
  boolean autoIncrement = false;
};
connection . name

返回数据库的 名称

connection . version

返回数据库的 版本

name 的 getter 步骤为返回 this 关联的 数据库名称

注意: name 属性即使 this关闭待定标志为 true 也不会变化。即该属性在 IDBDatabase 实例生命周期内保持不变。

version 的 getter 步骤为返回 this版本

这是否等同于 数据库版本 只要 连接是打开的,这属性与连接的 数据库版本一致。但一旦 连接关闭,此属性不会反映之后 升级事务带来的变更。
connection . objectStoreNames

返回数据库中所有 对象存储区的名称列表。

store = connection . createObjectStore(name [, options])

使用指定 nameoptions 创建新的 对象存储区并返回新的 IDBObjectStore

如果不是在 升级事务内调用,则抛出 "InvalidStateError" DOMException

connection . deleteObjectStore(name)

删除指定 name对象存储区

如果不是在 升级事务内调用,则抛出 "InvalidStateError" DOMException

objectStoreNames 的 getter 步骤如下:
  1. names列表,内容为 对象存储区名称,来自 对象存储区,属于 this对象存储区集合

  2. 返回用 创建排序名称列表处理 names 的结果(类型为 DOMStringList)。

这是否等同于 数据库对象存储区 名称 只要 连接是打开的,这属性与连接的 数据库对象存储区 名称一致。但一旦 连接关闭,此属性不会反映之后 升级事务带来的变更。

createObjectStore(name, options) 方法步骤如下:

  1. databasethis 关联的 数据库

  2. transactiondatabase升级事务(不为 null 时),否则 抛出 "InvalidStateError" DOMException

  3. transaction状态不是 活动,则 抛出 "TransactionInactiveError" DOMException

  4. keyPathoptionskeyPath 成员(不为 undefined 或 null 时),否则为 null。

  5. keyPath 不为 null 且不是 有效键路径,则 抛出 "SyntaxError" DOMException

  6. 对象存储区中已存在 name,则 抛出 "ConstraintError" DOMException

  7. autoIncrementoptionsautoIncrement 成员。

  8. autoIncrement 为 true 且 keyPath 为空字符串或任意序列(无论是否为空),则 抛出 "InvalidAccessError" DOMException

  9. storedatabase 中新建的 对象存储区。设其 名称name。若 autoIncrement 为 true,则该对象存储区使用 键生成器。若 keyPath 不为 null,则其 键路径keyPath

  10. 返回与 storetransaction 关联的新 对象存储区句柄

该方法在连接的 数据库中新建并返回指定名称的 对象存储区。注意,只能在 升级事务内调用此方法。

该方法会同步修改调用实例上的 objectStoreNames 属性。

在某些实现中,队列任务创建 对象存储区后,createObjectStore() 方法已经返回,但实现可能遇到问题(如异步插入新建对象存储区元数据,或因配额需用户授权)。此时仍需创建并返回 IDBObjectStore 对象。若创建对象存储区失败,必须用合适错误中止事务(如因配额失败则用 QuotaExceededError)。

deleteObjectStore(name) 方法步骤如下:

  1. databasethis 关联的 数据库

  2. transactiondatabase升级事务(不为 null 时),否则 抛出 "InvalidStateError" DOMException

  3. transaction状态不是 活动,则 抛出 "TransactionInactiveError" DOMException

  4. storedatabase 中名为 name对象存储区,否则 抛出 "NotFoundError" DOMException

  5. this对象存储区集合中移除 store

  6. 若有与 storetransaction 关联的 对象存储区句柄,则移除其 索引集合中的所有条目。

  7. 销毁 store

该方法销毁连接的 数据库中指定名称的 对象存储区。注意,只能在 升级事务内调用此方法。

该方法会同步修改调用实例上的 objectStoreNames 属性。

transaction = connection . transaction(scope [, mode [, options ] ])

新建一个 事务,作用域为 scope(可为单个 对象存储区名称或名称数组),mode("readonly" 或 "readwrite"),以及额外 options(如 durability 为 "default"、"strict" 或 "relaxed")。

默认 mode 为 "readonly",durability 默认值为 "default"。

connection . close()

在所有运行中的 事务结束后关闭 连接

transaction(storeNames, mode, options) 方法步骤如下:

  1. 如果该连接关联有存活升级事务抛出"InvalidStateError" DOMException

  2. 如果 this关闭待处理标志为 true,则 抛出 "InvalidStateError" DOMException

  3. scopestoreNames 是序列时的唯一字符串集合,否则为仅包含一个等于 storeNames 的字符串的集合。

  4. 如果 scope 中任何字符串不是该已连接数据库中的对象存储区名称,抛出 "NotFoundError" DOMException

  5. 如果 scope 为空,抛出 "InvalidAccessError" DOMException

  6. 如果 mode 不是 "readonly" 或 "readwrite", 抛出 TypeError

  7. transaction 为新创建事务,参数为本连接modeoptionsdurability成员,以及 scope 中命名的对象存储区集合。

  8. transaction清理事件循环为当前事件循环

  9. 返回表示 transactionIDBTransaction 对象。

🚧 durability 选项为本版本新增。 支持 Chrome 82, Edge 82, Firefox 126, Safari 15。 🚧

注意: 创建的 transaction 将遵循 生命周期规则。

close() 方法步骤如下:

  1. 用本连接运行关闭数据库连接

注意: 连接在所有未完成事务完成前不会真正关闭。后续调用 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 direction = "next");
  [NewObject] IDBRequest openKeyCursor(optional any query,
                                       optional IDBCursorDirection direction = "next");

  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 direction = "next";
};
store . name

返回该存储的名称

store . name = newName

将该存储的名称更新为newName

如果不是在升级事务中调用,则抛出"InvalidStateError" DOMException

store . keyPath

返回该存储的键路径,如果没有则返回 null。

store . indexNames

返回存储内所有索引的名称列表。

store . transaction

返回关联的事务

store . autoIncrement

如果该存储有键生成器则返回 true,否则返回 false。

name 的 getter 步骤为返回 this名称

这和对象存储区名称一样吗? 只要事务还没结束, 这里返回的就是关联对象存储区名称。但一旦事务结束,此属性不会反映后续升级事务修改的内容。

name 的 setter 步骤如下:

  1. name给定值

  2. transactionthis事务

  3. storethis对象存储区

  4. 如果 store 已被删除, 抛出 "InvalidStateError" DOMException

  5. 如果 transaction 不是升级事务抛出 "InvalidStateError" DOMException

  6. 如果 transaction状态不是active抛出 "TransactionInactiveError" DOMException

  7. 如果 store名称等于 name,则终止步骤。

  8. 如果 store数据库中已存在命名为name对象存储区抛出 "ConstraintError" DOMException

  9. store名称name

  10. this名称name

keyPath 的 getter 步骤为返回 this对象存储区键路径,若没有则为 null。键路径按 DOMString(若为字符串)或 sequence<DOMString>(若为字符串列表)转换,详见 [WEBIDL]

注意: 返回值并不是创建对象存储区时使用的同一个实例。但如果此属性返回对象(即 Array),每次访问时都是同一个对象实例。更改对象属性不会影响对象存储区

indexNames 的 getter 步骤如下:
  1. names列表,包含 索引this索引集合中的名称

  2. 返回用 names 创建排序名称列表的结果(即 DOMStringList)。

这和对象存储区的索引index名称列表一样吗? 只要事务还没结束, 这里返回的就是关联对象存储区的索引index名称列表。但一旦事务结束,此属性不会反映后续升级事务修改的内容。

transaction 的 getter 步骤为返回 this事务

autoIncrement 的 getter 步骤为:如果 this对象存储区键生成器则返回 true,否则返回 false。

下列方法在只读事务中调用会抛出"ReadOnlyError" DOMException,在事务激活时调用会抛出"TransactionInactiveError" DOMException
request = store . put(value [, key])
request = store . add(value [, key])

store 中用给定 valuekey 添加或更新记录

如果存储区使用内联键且指定了 key,会抛出 "DataError" DOMException

如果使用 put(),已存在的同键记录会被替换。如果使用 add(),且已存在同键记录,则 request 会失败,其 error 被设为 "ConstraintError" DOMException

成功时,requestresult 为记录的

request = store . delete(query)

删除 store 中与给定 键区间 query 匹配的记录

成功时,requestresultundefined

request = store . clear()

删除 store 中所有记录

成功时,requestresultundefined

put(value, key) 方法步骤为:返回运行 add or put,参数为 thisvaluekeyno-overwrite flag 为 false 的结果。

add(value, key) 方法步骤为:返回运行 add or put,参数为 thisvaluekeyno-overwrite flag 为 true 的结果。

add or put,参数为 handlevaluekeyno-overwrite flag,执行如下步骤:

  1. transactionhandle事务

  2. storehandle对象存储区

  3. 如果 store 已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. 如果 transaction只读事务抛出 "ReadOnlyError" DOMException

  6. 如果 store 使用内联键且给定了 key抛出 "DataError" DOMException

  7. 如果 store 使用外部键且没有键生成器且未给定 key抛出 "DataError" DOMException

  8. 如果给定了 key,则:

    1. r 为用 key 转换为键的结果。异常重新抛出。

    2. 如果 r 是 "invalid value" 或 "invalid type",抛出 "DataError" DOMException

    3. keyr

  9. targetRealm 为 user-agent 定义的 Realm

  10. clonevaluetargetRealm 下于 transaction 期间的 克隆。异常重新抛出。

    为什么要创建副本? 存储值时会序列化。这里当作副本处理允许规范其他算法把它当 ECMAScript 值,但如果行为差异不可观察,实际实现可优化。
  11. 如果 store 使用内联键,则:

    1. kpk 为用 clonestore键路径 提取键的结果。异常重新抛出。

    2. 如果 kpk 无效,抛出 "DataError" DOMException

    3. 如果 kpk 非 failure,令 keykpk

    4. 否则(kpk 为 failure):

      1. 如果 store 没有键生成器抛出 "DataError" DOMException

      2. 如果用 clonestore键路径 检查能否注入键返回 false, 抛出 "DataError" DOMException

  12. operation 为用 storeclonekeyno-overwrite flag 运行 存储记录到对象存储区的算法。

  13. 返回用 handleoperation 运行 异步执行请求的结果(IDBRequest)。

delete(query) 方法步骤如下:

  1. transactionthis事务

  2. storethis对象存储区

  3. 如果 store 已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. 如果 transaction只读事务抛出 "ReadOnlyError" DOMException

  6. range 为用 query 运行 转换为键区间(true)。异常重新抛出。

  7. operation 为用 storerange 运行 从对象存储区删除记录的算法。

  8. 返回用 thisoperation 运行 异步执行请求的结果(IDBRequest)。

注意: query 参数可以是键区间IDBKeyRange)用于定位要删除的记录

注意: 与其他接受键或键区间的方法不同,不允许此方法的 key 为 null。这样可减少小 bug 导致清空整个对象存储区的风险。

clear() 方法步骤如下:

  1. transactionthis事务

  2. storethis对象存储区

  3. 如果 store 已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. 如果 transaction只读事务抛出 "ReadOnlyError" DOMException

  6. operation 为用 store 运行 清空对象存储区的算法。

  7. 返回用 thisoperation 运行 异步执行请求的结果(IDBRequest)。

下列方法在事务激活时调用会抛出"TransactionInactiveError" DOMException
request = store . get(query)

检索第一个匹配 query 中给定键区间记录

成功时,requestresult,若无匹配记录则为 undefined

request = store . getKey(query)

检索第一个匹配 query 中给定键区间记录

成功时,requestresult,若无匹配记录则为 undefined

request = store . getAll(query [, count])
request = store . getAll({query, count, direction})

检索所有匹配 query 中给定键区间记录(若给定 count 则最多返回该数量)。 direction 选项设为 "next" 可返回前 count 个值,设为 "prev" 可返回最后 count 个值。

成功时,requestresult 为包含所有Array

request = store . getAllKeys(query [, count])
request = store . getAllKeys({query, count, direction})

检索所有匹配 query 中给定键区间记录(若给定 count 则最多返回该数量)。 direction 选项设为 "next" 可返回前 count 个键,设为 "prev" 可返回最后 count 个键。

成功时,requestresult 为包含所有Array

request = store . getAllRecords({query, count, direction})

检索记录

query 选项指定要匹配的键区间count 限制匹配记录数量。direction 设为 "next" 可返回前 count 个记录,设为 "prev" 可返回最后 count 个记录。

成功时,requestresultArray,每个成员为 IDBRecord

request = store . count(query)

检索匹配 query 中给定键区间记录数量。

成功时,requestresult 为记录数。

get(query) 方法步骤如下:

  1. transactionthis事务

  2. storethis对象存储区

  3. 如果 store 已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. range 为用 query 运行 转换为键区间(true)。异常重新抛出。

  6. operation 为用 当前 Realm 记录storerange 运行 从对象存储区检索值的算法。

  7. 返回用 thisoperation 运行 异步执行请求的结果(IDBRequest)。

注意: query 参数可以是键区间IDBKeyRange),用于定位要检索的记录值。若指定区间,则检索该区间内第一个现有值。

注意: 如果给定键不存在记录,该方法的结果与存在记录但值为 undefined 的情况相同。若需区分两者,可用 openCursor() 方法,若有记录则游标值为 undefined,否则无游标。

getKey(query) 方法步骤如下:

  1. transactionthis事务

  2. storethis对象存储区

  3. 如果 store 已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. range 为用 query 运行 转换为键区间(true)。异常重新抛出。

  6. operation 为用 storerange 运行 从对象存储区检索键的算法。

  7. 返回用 thisoperation 运行 异步执行请求的结果(IDBRequest)。

注意: query 参数可以是键区间IDBKeyRange)用于定位要检索的记录键。若指定区间,则检索该区间内第一个现有键。

getAll(queryOrOptions, count) 方法步骤如下:

  1. 返回用 创建多项检索请求,参数为 当前 Realm 记录this、"value"、queryOrOptions 和(如给定)count 的结果。异常重新抛出。

getAllKeys(queryOrOptions, count) 方法步骤如下:

  1. 返回用 创建多项检索请求,参数为 当前 Realm 记录this、"key"、queryOrOptions 和(如给定)count 的结果。异常重新抛出。

getAllRecords(options) 方法步骤如下:

  1. 返回用 创建多项检索请求,参数为 当前 Realm 记录this、"record" 和 options 的结果。异常重新抛出。

count(query) 方法步骤如下:

  1. transactionthis事务

  2. storethis对象存储区

  3. 如果 store 已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. range 为用 query 运行 转换为键区间。异常重新抛出。

  6. operation 为用 storerange 运行 统计区间内记录数的算法。

  7. 返回用 thisoperation 运行 异步执行请求的结果(IDBRequest)。

注意: query 参数可以是键区间IDBKeyRange),用于定位要计数的记录。若为 null 或未给定,则使用无限键区间

下列方法在事务激活时调用会抛出"TransactionInactiveError" DOMException
request = store . openCursor([query [, direction = "next"]])

打开一个游标,遍历所有与 query 匹配的记录,按 direction 顺序排列。如果 query 为 null,则匹配 store 中所有记录

成功时,requestresult 为指向第一个匹配记录IDBCursorWithValue,若无匹配记录则为 null。

request = store . openKeyCursor([query [, direction = "next"]])

打开一个游标,并设置仅键标志为 true,遍历所有与 query 匹配的记录,按 direction 顺序排列。如果 query 为 null,则匹配 store 中所有记录

成功时,requestresult 为指向第一个匹配记录IDBCursor,若无匹配记录则为 null。

openCursor(query, direction) 方法步骤如下:

  1. transactionthis事务

  2. storethis对象存储区

  3. 如果 store 已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. range 为用 query 运行 转换为键区间。异常重新抛出。

  6. cursor 为新游标,其源句柄this位置为 undefined,方向direction已取值标志为 false,为 undefined,区间range仅键标志为 false。

  7. operation 为用 遍历游标,参数为 当前 Realm 记录cursor

  8. request 为用 异步执行请求,参数为 thisoperation 的结果。

  9. cursorrequestrequest

  10. 返回 request

注意: query 参数可以是键区间IDBKeyRange),用于指定游标区间。如为 null 或未给定,则使用无限键区间

openKeyCursor(query, direction) 方法步骤如下:

  1. transactionthis事务

  2. storethis对象存储区

  3. 如果 store 已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. range 为用 query 运行 转换为键区间。异常重新抛出。

  6. cursor 为新游标,其源句柄this位置为 undefined,方向direction已取值标志为 false,为 undefined,区间range仅键标志为 true。

  7. operation 为用 遍历游标,参数为 当前 Realm 记录cursor

  8. request 为用 异步执行请求,参数为 thisoperation 的结果。

  9. cursorrequestrequest

  10. 返回 request

注意: query 参数可以是键区间IDBKeyRange),用于指定游标区间。如为 null 或未给定,则使用无限键区间

index = store . index(name)

返回 store 中命名为 nameIDBIndex

index = store . createIndex(name, keyPath [, options])

store 中创建一个新的 索引,指定 namekeyPathoptions,并返回新的 IDBIndex。如果 keyPathoptions 定义的约束无法满足 store 中已存在的数据,则 升级事务中止,抛出 "ConstraintError" DOMException

如果不是在升级事务中调用,则抛出"InvalidStateError" DOMException

store . deleteIndex(name)

删除 store 中命名为 name索引

如果不是在升级事务中调用,则抛出"InvalidStateError" DOMException

createIndex(name, keyPath, options) 方法步骤如下:

  1. transactionthis事务

  2. storethis对象存储区

  3. 如果 transaction 不是升级事务抛出 "InvalidStateError" DOMException

  4. 如果 store 已被删除,抛出 "InvalidStateError" DOMException

  5. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  6. 如果 store 中已存在名为 name索引抛出 "ConstraintError" DOMException

  7. 如果 keyPath 不是有效键路径抛出 "SyntaxError" DOMException

  8. uniqueoptionsunique 成员。

  9. multiEntryoptionsmultiEntry 成员。

  10. 如果 keyPath 是序列且 multiEntry 为 true,抛出 "InvalidAccessError" DOMException

  11. indexstore 中的新索引。设 index名称name键路径keyPath唯一标志uniquemultiEntry 标志multiEntry

  12. index 添加到 this索引集合

  13. 返回与 indexthis 关联的新索引句柄

该方法在对象存储区中新建并返回具有指定名称的索引。注意此方法必须仅在升级事务中调用。

请求创建的索引可以对索引引用的对象存储区数据设置约束, 例如要求索引键路径所引用的值唯一。如果引用对象存储区已有数据违反这些约束,不能导致 createIndex() 抛出异常或影响其返回结果。实现仍需创建并返回 IDBIndex 对象,并且实现必须 排队数据库任务以中止用于 createIndex() 调用的升级事务

此方法同步修改调用的 IDBObjectStore 实例上的 indexNames 属性。虽然此方法不返回 IDBRequest 对象,但索引创建本身会作为 升级事务中的异步请求处理。

在某些实现中,createIndex 方法返回后,异步创建索引时可能会遇到问题。例如在新索引的元数据异步插入数据库、或因配额需要请求用户权限的情况下。此类实现仍需创建并返回 IDBIndex 对象,一旦确认创建索引失败,必须使用合适错误运行 中止事务步骤。例如因配额失败需用 QuotaExceededError,因唯一性约束无法创建索引时需用 "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() 时,两个 请求都未执行。第二个请求执行时会创建重复名称。由于索引创建被视为异步请求,索引的唯一性约束不会导致第二个 请求失败。相反,当索引创建且约束失败时,事务会被中止

index(name) 方法步骤如下:

  1. transactionthis事务

  2. storethis对象存储区

  3. 如果 store 已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态finished抛出 "InvalidStateError" DOMException

  5. index索引集合中名为 name索引,如不存在则 抛出 "NotFoundError" DOMException

  6. 返回与 indexthis 关联的索引句柄

注意: 多次在同一 IDBObjectStore 实例上用同名调用该方法,返回的都是同一个 IDBIndex 实例。

注意: 返回的 IDBIndex 实例仅关联于当前 IDBObjectStore 实例。在不同实例上调用同名方法会返回不同的 IDBIndex 实例。

deleteIndex(name) 方法步骤如下:

  1. transactionthis事务

  2. storethis对象存储区

  3. 如果 transaction 不是升级事务抛出 "InvalidStateError" DOMException

  4. 如果 store 已被删除,抛出 "InvalidStateError" DOMException

  5. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  6. indexstore 中名为 name索引,如不存在则 抛出 "NotFoundError" DOMException

  7. this索引集合中移除 index

  8. 销毁 index

此方法销毁对象存储区中指定名称的索引。注意此方法必须仅在升级事务中调用。

该方法同步修改调用的 IDBObjectStore 实例上的 indexNames 属性。虽然此方法不返回 IDBRequest 对象,但索引删除本身会作为 升级事务中的异步请求处理。

4.6. IDBIndex 接口

IDBIndex 接口表示一个索引句柄

[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 direction = "next");
  [NewObject] IDBRequest openKeyCursor(optional any query,
                                       optional IDBCursorDirection direction = "next");
};
index . name

返回索引的名称

index . name = newName

将索引的名称更新为newName

如果不是在升级事务中调用,则抛出"InvalidStateError" DOMException

index . objectStore

返回索引所属的 IDBObjectStore

index . keyPath

返回索引的键路径

index . multiEntry

如果索引的multiEntry 标志为 true,则返回 true。

index . unique

如果索引的唯一标志为 true,则返回 true。

name 的 getter 步骤为返回 this名称

索引名称一样吗? 只要事务还没结束, 这里返回的就是关联索引名称。但一旦事务结束,此属性不会反映后续升级事务修改的内容。

name 的 setter 步骤如下:

  1. name给定值

  2. transactionthis事务

  3. indexthis索引

  4. 如果 transaction 不是升级事务抛出 "InvalidStateError" DOMException

  5. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  6. 如果 index 或其对象存储区已被删除,抛出 "InvalidStateError" DOMException

  7. 如果 index名称等于 name,则终止步骤。

  8. 如果 index对象存储区中已存在名为 name索引抛出 "ConstraintError" DOMException

  9. index名称name

  10. this名称name

objectStore 的 getter 步骤为返回 this对象存储区句柄

keyPath 的 getter 步骤为返回 this索引键路径。键路径按 DOMString(字符串)或 sequence<DOMString>(字符串列表)转换,详见 [WEBIDL]

注意: 返回值不是创建索引时使用的同一实例。但如果此属性返回对象(即 Array),每次访问都是同一个对象实例。更改对象属性不会影响索引

multiEntry 的 getter 步骤为返回 this索引multiEntry 标志

unique 的 getter 步骤为返回 this索引唯一标志

下列方法在事务激活时调用会抛出"TransactionInactiveError" DOMException
request = index . get(query)

检索第一个匹配 query 中给定键区间记录

成功时,requestresult,若无匹配记录则为 undefined

request = index . getKey(query)

检索与query中给定键范围匹配的第一个记录

成功时,requestresult,若无匹配记录则为 undefined

request = index . getAll(query [, count])
request = index . getAll({query, count, direction})

检索所有匹配 query 中给定键区间记录(若给定 count 则最多返回该数量)。 direction 选项设为 "next" 可返回前 count 个值,"prev" 可返回最后 count 个值。设为 "nextunique" 或 "prevunique" 可在匹配到重复索引键后仅返回第一个记录,忽略后续重复键。

成功时,requestresult 为包含所有Array

request = index . getAllKeys(query [, count])
request = index . getAllKeys({query, count, direction})

检索所有匹配 query 中给定键区间记录(若给定 count 则最多返回该数量)。 direction 选项设为 "next" 可返回前 count 个键,"prev" 可返回最后 count 个键。设为 "nextunique" 或 "prevunique" 可在匹配到重复索引键后仅返回第一个记录,忽略后续重复键。

成功时,requestresult 为包含所有Array

request = index . getAllRecords({query, count, direction})

检索记录和索引

query 选项指定要匹配的键区间count 限制匹配记录数量。direction 设为 "next" 可返回前 count 个记录,"prev" 可返回最后 count 个记录。设为 "nextunique" 或 "prevunique" 可跳过重复索引键,仅返回第一个记录。

成功时,requestresultArray,每个成员为 IDBRecord。通过 IDBRecord’s key 获取记录的索引键,通过 IDBRecord’s primaryKey 获取主键。

request = index . count(query)

检索匹配 query 中给定键区间记录数量。

成功时,requestresult 为记录数。

get(query) 方法步骤如下:

  1. transactionthis事务

  2. indexthis索引

  3. 如果 index 或其 对象存储区已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. range 为用 query 运行 转换为键区间(true)。异常重新抛出。

  6. operation 为用 从索引检索引用值,参数为 当前 Realm 记录indexrange 的算法。

  7. 返回用 thisoperation 运行 异步执行请求的结果(IDBRequest)。

注意: query 参数可以是键区间IDBKeyRange),用于定位要检索的引用值。如指定区间,则检索该区间内第一个现有记录。

注意: 若指定键无记录,该方法的结果与存在记录但值为 undefined 的情况相同。若需区分两者,可用 openCursor(),若有记录游标值为 undefined,否则无游标。

getKey(query) 方法步骤如下:

  1. transactionthis事务

  2. indexthis索引

  3. 如果 index 或其 对象存储区已被删除, 抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. range 为用 query 运行 转换为键区间(true)。异常重新抛出。

  6. operation 为用 indexrange 运行 从索引检索值的算法。

  7. 返回用 thisoperation 运行 异步执行请求的结果(IDBRequest)。

注意: query 参数可以是键区间IDBKeyRange),用于定位要检索的记录键。如指定区间,则检索该区间内第一个现有键。

getAll(queryOrOptions, count) 方法步骤如下:

  1. 返回用 创建多项检索请求,参数为 当前 Realm 记录this、"value"、queryOrOptions 和(如给定)count 的结果。异常重新抛出。

getAllKeys(queryOrOptions, count) 方法步骤如下:

  1. 返回用 创建多项检索请求,参数为 当前 Realm 记录this、"key"、queryOrOptions 和(如给定)count 的结果。异常重新抛出。

getAllRecords(options) 方法步骤如下:

  1. 返回用 创建多项检索请求,参数为 当前 Realm 记录this、"record" 和 options 的结果。异常重新抛出。

count(query) 方法步骤如下:

  1. transactionthis事务

  2. indexthis索引

  3. 如果 index 或其 对象存储区已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. range 为用 query 运行 转换为键区间。异常重新抛出。

  6. operation 为用 indexrange 运行 统计区间内记录数的算法。

  7. 返回用 thisoperation 运行 异步执行请求的结果(IDBRequest)。

注意: query 参数可以是键区间IDBKeyRange),用于定位要计数的记录。如为 null 或未给定,则使用无限键区间

下列方法在事务激活时调用会抛出"TransactionInactiveError" DOMException
request = index . openCursor([query [, direction = "next"]])

打开一个游标,遍历所有与 query 匹配的记录,按 direction 顺序排列。如果 query 为 null,则匹配 index 中所有记录

成功时,requestresultIDBCursorWithValue,若无匹配记录则为 null。

request = index . openKeyCursor([query [, direction = "next"]])

打开一个游标,并设置仅键标志为 true,遍历所有与 query 匹配的记录,按 direction 顺序排列。如果 query 为 null,则匹配 index 中所有记录

成功时,requestresultIDBCursor,若无匹配记录则为 null。

openCursor(query, direction) 方法步骤如下:

  1. transactionthis事务

  2. indexthis索引

  3. 如果 index 或其 对象存储区已被删除, 抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. range 为用 query 运行 转换为键区间。异常重新抛出。

  6. cursor 为新游标,其源句柄this位置为 undefined,方向direction已取值标志为 false,为 undefined,区间range仅键标志为 false。

  7. operation 为用 遍历游标,参数为 当前 Realm 记录cursor

  8. request 为用 异步执行请求,参数为 thisoperation 的结果。

  9. cursorrequestrequest

  10. 返回 request

注意: query 参数可以是键区间IDBKeyRange),用于指定游标区间。如为 null 或未给定,则使用无限键区间

openKeyCursor(query, direction) 方法步骤如下:

  1. transactionthis事务

  2. indexthis索引

  3. 如果 index 或其 对象存储区已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 transaction状态不是 active抛出 "TransactionInactiveError" DOMException

  5. range 为用 query 运行 转换为键区间。异常重新抛出。

  6. cursor 为新游标,其源句柄this位置为 undefined,方向direction已取值标志为 false,为 undefined,区间range仅键标志为 true。

  7. operation 为用 遍历游标,参数为 当前 Realm 记录cursor

  8. request 为用 异步执行请求,参数为 thisoperation 的结果。

  9. cursorrequestrequest

  10. 返回 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);
};
range . lower

返回区间的下界,如果没有则为 undefined

range . upper

返回区间的上界,如果没有则为 undefined

range . lowerOpen

返回区间的下界开放标志

range . upperOpen

返回区间的上界开放标志

lower 的 getter 步骤为:如果 下界不为 null,则用 将键转换为值,否则返回 undefined。

upper 的 getter 步骤为:如果 上界不为 null,则用 将键转换为值,否则返回 undefined。

lowerOpen 的 getter 步骤为:返回 下界开放标志

upperOpen 的 getter 步骤为:返回 上界开放标志

range = IDBKeyRange . only(key)

返回一个仅包含 key 的新的 IDBKeyRange

range = IDBKeyRange . lowerBound(key [, open = false])

返回一个下界为 key、无上界的新 IDBKeyRange。如果 open 为 true,则 key 不包含在区间内。

range = IDBKeyRange . upperBound(key [, open = false])

返回一个无下界、上界为 key 的新 IDBKeyRange。如果 open 为 true,则 key 不包含在区间内。

range = IDBKeyRange . bound(lower, upper [, lowerOpen = false [, upperOpen = false]])

返回一个从 lowerupper 的新 IDBKeyRange。如果 lowerOpen 为 true,则下界不包含在区间内;如果 upperOpen 为 true,则上界不包含在区间内。

only(value) 方法步骤如下:

  1. key 为用 value 转换为键的结果。异常重新抛出。

  2. 如果 key 为 "invalid value" 或 "invalid type",抛出 "DataError" DOMException

  3. 创建并返回一个仅包含 key键区间

lowerBound(lower, open) 方法步骤如下:

  1. lowerKey 为用 lower 转换为键的结果。异常重新抛出。

  2. 如果 lowerKey 无效,抛出 "DataError" DOMException

  3. 创建并返回一个下界为 lowerKey、下界开放标志为 open、上界为 null、上界开放标志为 true 的键区间

upperBound(upper, open) 方法步骤如下:

  1. upperKey 为用 upper 转换为键的结果。异常重新抛出。

  2. 如果 upperKey 为 "invalid value" 或 "invalid type",抛出 "DataError" DOMException

  3. 创建并返回一个下界为 null、下界开放标志为 true、上界为 upperKey、上界开放标志为 open键区间

bound(lower, upper, lowerOpen, upperOpen) 方法步骤如下:

  1. lowerKey 为用 lower 转换为键的结果。异常重新抛出。

  2. 如果 lowerKey 为 "invalid value" 或 "invalid type",抛出 "DataError" DOMException

  3. upperKey 为用 upper 转换为键的结果。异常重新抛出。

  4. 如果 upperKey 为 "invalid value" 或 "invalid type",抛出 "DataError" DOMException

  5. 如果 lowerKey 大于 upperKey抛出 "DataError" DOMException

  6. 创建并返回一个下界为 lowerKey、下界开放标志为 lowerOpen、上界为 upperKey、上界开放标志为 upperOpen键区间

range . includes(key)

如果 key 在区间内则返回 true,否则返回 false。

includes(key) 方法步骤如下:

  1. k 为用 key 转换为键的结果。异常重新抛出。

  2. 如果 k 为 "invalid value" 或 "invalid type",抛出 "DataError" DOMException

  3. 如果 k 区间内则返回 true,否则返回 false。

4.8. IDBRecord 接口

IDBRecord 接口表示一个记录快照

[Exposed=(Window,Worker)]
interface IDBRecord {
  readonly attribute any key;
  readonly attribute any primaryKey;
  readonly attribute any value;
};
record . key

返回记录的

record . primaryKey

如果记录是从索引获取的,返回在索引引用对象存储区中的记录键。

如果记录是从对象存储区获取的,返回记录的,与 record.key 相同。

record . value

返回记录的

key 的 getter 步骤为用 将键转换为值,参数为 this

primaryKey 的 getter 步骤为用 将键转换为值,参数为 this主键

value 的 getter 步骤为返回 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

返回游标打开自的 IDBObjectStoreIDBIndex

cursor . direction

返回游标的方向 ("next", "nextunique", "prev" 或 "prevunique")。

cursor . key

返回游标的。 若游标正在前进或已结束,抛出 "InvalidStateError" DOMException

cursor . primaryKey

返回游标的有效键。 若游标正在前进或已结束,抛出 "InvalidStateError" DOMException

cursor . request

返回用于获取此游标的请求

source 的 getter 步骤为返回 this源句柄

注意: source 属性即使游标正在迭代、已迭代到末尾或其 事务激活,也不会返回 null 或抛出异常。

direction 的 getter 步骤为返回 this方向

key 的 getter 步骤为用游标当前的运行 将键转换为值 的结果。

注意: 如果 key 返回对象(如 DateArray),则在游标的改变前,每次访问都是同一个对象实例。修改该对象会被所有访问该值的人看到,但不会修改数据库内容。

primaryKey 的 getter 步骤为用游标当前的有效键运行 将键转换为值 的结果。

注意: 如果 primaryKey 返回对象(如 DateArray),则在游标的有效键改变前,每次访问都是同一个对象实例。修改该对象会被所有访问该值的人看到,但不会修改数据库内容。

request 的 getter 步骤为返回 this请求

🚧 request 属性为本版本新增。 支持 Chrome 76、Edge 79、Firefox 77 和 Safari 15。 🚧
下列方法用于推进游标。 游标推进后,将在打开游标时返回的 success 事件中触发。 result 如果有记录在区间内,则为同一个游标,否则为 undefined

若游标正在前进时调用这些方法,将抛出 "InvalidStateError" DOMException

下列方法在事务激活时调用会抛出"TransactionInactiveError" DOMException

cursor . advance(count)

将游标推进到区间内下一个 count记录

cursor . continue()

将游标推进到区间内下一个记录

cursor . continue(key)

将游标推进到区间内下一个匹配或大于 key记录

cursor . continuePrimaryKey(key, primaryKey)

将游标推进到区间内下一个匹配或大于 keyprimaryKey记录。若不是索引,则抛出 "InvalidAccessError" DOMException

advance(count) 方法步骤如下:

  1. 如果 count 为 0,抛出 TypeError

  2. transactionthis事务

  3. 如果 transaction状态不是 激活抛出 "TransactionInactiveError" DOMException

  4. 如果 this有效对象存储区已被删除,抛出 "InvalidStateError" DOMException

  5. 如果 this已取值标志为 false(表示游标正在迭代或已到末尾), 抛出 "InvalidStateError" DOMException

  6. this已取值标志为 false。

  7. requestthis请求

  8. request已处理标志为 false。

  9. request完成标志为 false。

  10. operation 为用 遍历游标,参数为 当前 Realm 记录thiscount

  11. 异步执行请求,参数为 this源句柄operationrequest

注意: 在新的游标数据加载前多次调用此方法(如在同一个 onsuccess 回调里连续调用两次 advance()),第二次调用会因游标的已取值标志被设为 false 而抛出 "InvalidStateError" DOMException

continue(key) 方法步骤如下:

  1. transactionthis事务

  2. 如果 transaction状态不是 激活抛出 "TransactionInactiveError" DOMException

  3. 如果 this有效对象存储区已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 this已取值标志为 false(游标正在迭代或已到末尾), 抛出 "InvalidStateError" DOMException

  5. 如果给定了 key,则:

    1. r 为用 key 转换为键的结果。异常重新抛出。

    2. 如果 r 为 "invalid value" 或 "invalid type",抛出 "DataError" DOMException

    3. keyr

    4. 如果 key 小于等于 this位置,且 this方向为 "next" 或 "nextunique", 抛出 "DataError" DOMException

    5. 如果 key 大于等于 this位置,且 this方向为 "prev" 或 "prevunique", 抛出 "DataError" DOMException

  6. this已取值标志为 false。

  7. requestthis请求

  8. request已处理标志为 false。

  9. request完成标志为 false。

  10. operation 为用 遍历游标,参数为 当前 Realm 记录this 和(如给定)key

  11. 异步执行请求,参数为 this源句柄operationrequest

注意: 在新的游标数据加载前多次调用此方法(如在同一个 onsuccess 回调里连续调用两次 continue()),第二次调用会因游标的已取值标志被设为 false 而抛出 "InvalidStateError" DOMException

continuePrimaryKey(key, primaryKey) 方法步骤如下:

  1. transactionthis事务

  2. 如果 transaction状态不是 激活抛出 "TransactionInactiveError" DOMException

  3. 如果 this有效对象存储区已被删除,抛出 "InvalidStateError" DOMException

  4. 如果 this不是索引抛出 "InvalidAccessError" DOMException

  5. 如果 this方向不是 "next" 或 "prev", 抛出 "InvalidAccessError" DOMException

  6. 如果 this已取值标志为 false(游标正在迭代或已到末尾), 抛出 "InvalidStateError" DOMException

  7. r 为用 key 转换为键的结果。异常重新抛出。

  8. 如果 r 为 "invalid value" 或 "invalid type",抛出 "DataError" DOMException

  9. keyr

  10. r 为用 primaryKey 转换为键的结果。异常重新抛出。

  11. 如果 r 为 "invalid value" 或 "invalid type",抛出 "DataError" DOMException

  12. primaryKeyr

  13. 如果 key 小于 this位置,且 this方向为 "next", 抛出 "DataError" DOMException

  14. 如果 key 大于 this位置,且 this方向为 "prev", 抛出 "DataError" DOMException

  15. 如果 key 等于 this位置,且 primaryKey 小于等于 this对象存储区位置,且 this方向为 "next", 抛出 "DataError" DOMException

  16. 如果 key 等于 this位置,且 primaryKey 大于等于 this对象存储区位置,且 this方向为 "prev", 抛出 "DataError" DOMException

  17. this已取值标志为 false。

  18. requestthis请求

  19. request已处理标志为 false。

  20. request完成标志为 false。

  21. operation 为用 遍历游标,参数为 当前 Realm 记录thiskeyprimaryKey

  22. 异步执行请求,参数为 this源句柄operationrequest

注意: 在新的游标数据加载前多次调用此方法(如在同一个 onsuccess 回调里连续调用两次 continuePrimaryKey()),第二次调用会因游标的已取值标志被设为 false 而抛出 "InvalidStateError" DOMException

下列方法在只读事务中调用会抛出"ReadOnlyError" DOMException, 在事务激活时调用会抛出"TransactionInactiveError" DOMException
request = cursor . update(value)

用新值更新游标指向的记录

如果有效对象存储区使用内嵌键发生变化,则抛出"DataError" DOMException

成功后,requestresult 为该记录

request = cursor . delete()

删除游标指向的记录

成功后,requestresultundefined

update(value) 方法步骤如下:

  1. transactionthis事务

  2. 如果 transaction状态不是 激活抛出 "TransactionInactiveError" DOMException

  3. 如果 transaction只读事务抛出 "ReadOnlyError" DOMException

  4. 如果 this有效对象存储区已被删除,抛出 "InvalidStateError" DOMException

  5. 如果 this已取值标志为 false(游标正在迭代或已到末尾), 抛出 "InvalidStateError" DOMException

  6. 如果 this仅键标志为 true,抛出 "InvalidStateError" DOMException

  7. targetRealm 为用户代理定义的 Realm

  8. clonevaluetransaction 期间于 targetRealm副本。异常重新抛出。

    为什么要创建副本? 值存储时会序列化。此处视为副本,允许规范中其它算法将其视为 ECMAScript 值,但实现可优化,只要行为不可被观察到。
  9. 如果 this有效对象存储区使用内嵌键

    1. kpk 为用 clone键路径(属于 this有效对象存储区)运行 按键路径从值提取键。异常重新抛出。

    2. 如果 kpk 失败、无效或不等于 this有效键抛出 "DataError" DOMException

  10. operation 为用 存储记录到对象存储区,参数为 this有效对象存储区clonethis有效键 和 false。

  11. 返回用 thisoperation 运行 异步执行请求的结果(IDBRequest)。

注意: 调用存储记录到对象存储区的结果是,如果游标移动后该记录已被删除,则会新建记录。

delete() 方法步骤如下:

  1. transactionthis事务

  2. 如果 transaction状态不是 激活抛出 "TransactionInactiveError" DOMException

  3. 如果 transaction只读事务抛出 "ReadOnlyError" DOMException

  4. 如果 this有效对象存储区已被删除,抛出 "InvalidStateError" DOMException

  5. 如果 this已取值标志为 false(游标正在迭代或已到末尾), 抛出 "InvalidStateError" DOMException

  6. 如果 this仅键标志为 true,抛出 "InvalidStateError" DOMException

  7. operation 为用 从对象存储区删除记录,参数为 this有效对象存储区this有效键

  8. 返回用 thisoperation 运行 异步执行请求的结果(IDBRequest)。

游标仅键标志为 false 时,也实现 IDBCursorWithValue 接口。

[Exposed=(Window,Worker)]
interface IDBCursorWithValue : IDBCursor {
  readonly attribute any value;
};
cursor . value

返回游标当前的

value 的 getter 步骤为返回 this 当前的

注意: 如果 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

返回事务创建时的持久性提示("strict"、 "relaxed"), 或 "default"。

transaction . db

返回事务的连接

transaction . error

如果事务被中止,返回提供原因的错误(DOMException)。

objectStoreNames 的 getter 步骤如下:

  1. names列表,内容为 范围内所有对象存储区名称

  2. 返回用 names 运行创建已排序名称列表的结果(DOMStringList)。

注意: 此属性每次返回的列表内容不会改变,但在升级事务期间,多次调用可能随对象存储区的创建和删除返回不同内容。

mode 的 getter 步骤为返回 this模式

durability 的 getter 步骤为返回 this持久性提示

🚧 durability 属性为本版本新增。 支持 Chrome 82、Edge 82、Firefox 126 和 Safari 15。 🚧

db 的 getter 步骤为返回 this连接关联的数据库

error 的 getter 步骤为返回 this错误,如果没有则为 null。

注意: 如果该事务因请求失败被中止,则此错误与请求错误相同。若因事件处理器未捕获异常中止,则为 "AbortError" DOMException。若因提交时错误中止,则反映失败原因(如 QuotaExceededError、 "ConstraintError" 或 "UnknownError" DOMException)。

transaction . objectStore(name)

返回事务范围内的 IDBObjectStore

transaction . abort()

中止事务。所有待处理请求将以"AbortError" DOMException失败,所有对数据库的更改将被回滚。

transaction . commit()

尝试提交事务。所有待处理请求将允许完成,但不再接受新请求。可用于强制事务快速完成,无需等待待处理请求触发 success 事件再正常提交。

如待处理请求失败(如约束错误),事务将中止。成功请求的 success 事件仍会触发,但事件处理器抛出异常不会导致事务中止。类似地,失败请求的 error 事件仍会触发,但调用 preventDefault() 不会阻止事务中止。

objectStore(name) 方法步骤如下:

  1. 如果 this状态已完成抛出 "InvalidStateError" DOMException

  2. store范围内名称为 name对象存储区,如无则 抛出 "NotFoundError" DOMException

  3. 返回与 storethis 关联的对象存储区句柄

注意: 在同一个 IDBTransaction 实例上用同名多次调用该方法,返回的是同一个 IDBObjectStore 实例。

注意: 返回的 IDBObjectStore 实例仅关联当前 IDBTransaction。在不同 IDBTransaction 上调用会返回不同的 IDBObjectStore 实例。

abort() 方法步骤如下:

  1. 如果 this状态提交中已完成抛出 "InvalidStateError" DOMException

  2. 使用 终止一个事务,参数为 this 和 null。

commit() 方法的步骤如下:

  1. 如果 this状态 不是 active,则 抛出一个 "InvalidStateError" DOMException

  2. 使用 提交一个事务,参数为 this

🚧 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、数据库 versionrequest 打开数据库连接,请按以下步骤执行:

  1. queuestorageKeyname连接队列

  2. request 添加到 queue

  3. 等待 queue 中所有先前的请求处理完毕。

  4. dbstorageKey数据库中名称为 name 的数据库,否则为 null。

  5. 如果 version 未定义,则如果 db 为 null 设为 1,否则设为 db版本

  6. 如果 db 为 null,令 db 为新建的数据库名称name版本为 0,且无对象存储区。如失败,返回相应错误(如 QuotaExceededError 或 "UnknownError" DOMException)。

  7. 如果 db版本大于 version,则返回新创建的 "VersionError" DOMException,并终止步骤。

  8. connection 为到 db 的新连接

  9. connection版本version

  10. 如果 db版本小于 version,则:

    1. openConnections 为所有与 db 相关但不包括 connection连接集合

    2. 遍历 openConnections 中未设置待关闭标志为 true 的 entry排队数据库任务触发版本变更事件,事件名为 versionchange,目标为 entry,参数为 db版本version

      注意: 触发该事件可能导致 openConnections 中部分对象被关闭,此时不会对这些对象再次触发 versionchange 事件,即使还没触发过。

    3. 等待所有事件触发完毕。

    4. 如果 openConnections 中有连接仍未关闭,排队数据库任务触发版本变更事件,事件名为 blocked,目标为 request,参数为 db版本version

    5. 等待 openConnections 中所有连接关闭

    6. connectionversionrequest运行 升级数据库

    7. 如果 connection关闭, 返回新创建的 "AbortError" DOMException,并终止步骤。

    8. 如果 requesterror 已设置,则使用 connection 执行 关闭数据库连接的步骤,返回一个新创建的 "AbortError" DOMException,并中止这些步骤。

  11. 返回 connection

5.2. 关闭数据库连接

要用 connection 对象和可选的 forced flag 关闭数据库连接,请按以下步骤执行:

  1. connection待关闭标志为 true。

  2. 如果 forced flag 为 true,则对每个通过 connection 创建transaction,运行 中止事务,参数为 transaction 和新创建的 "AbortError" DOMException

  3. 等待所有通过 connection 创建的事务完成。完成后,connection关闭

  4. 如果 forced flag 为 true,则触发事件,事件名为 close,目标为 connection

    注意: 只有连接异常关闭时才会触发 close 事件,例如存储键的存储被清除、损坏或 I/O 错误。如果显式调用 close(),则不会触发该事件。

注意: 一旦连接待关闭标志设为 true,就不能再通过该连接创建新事务。所有会创建事务的方法会先检查连接的待关闭标志,如果为 true 就抛出异常。

注意: 一旦连接关闭,将会解除对升级数据库删除数据库步骤的阻塞,这两者等待所有连接关闭后继续。

5.3. 删除数据库

要用请求删除数据库的 storageKey、数据库 namerequest 删除数据库,请按以下步骤执行:

  1. queuestorageKeyname连接队列

  2. request 添加到 queue

  3. 等待 queue 中所有先前的请求处理完毕。

  4. dbstorageKey 中名称为 name数据库,如不存在则返回 0。

  5. openConnections 为所有与 db 关联的连接集合

  6. 遍历 openConnections 中未设置待关闭标志为 true 的 entry排队数据库任务触发版本变更事件,事件名为 versionchange,目标为 entry,参数为 db版本和 null。

    注意: 触发该事件可能导致 openConnections 中部分对象被关闭,此时不会对这些对象再次触发 versionchange 事件,即使还没触发过。

  7. 等待所有事件触发完毕。

  8. 如果 openConnections 中有连接仍未关闭,排队数据库任务触发版本变更事件,事件名为 blocked,目标为 request,参数为 db版本和 null。

  9. 等待 openConnections 中所有连接关闭

  10. versiondb版本

  11. 删除 db。如失败,返回相应错误(如 QuotaExceededError 或 "UnknownError" DOMException)。

  12. 返回 version

5.4. 提交事务

要用待提交的 transaction 提交事务,请按以下步骤执行:

  1. transaction状态 设置为 committing

  2. 并行运行以下步骤:

    1. 等待 transaction请求列表中的每一项都已被处理

    2. 如果 transaction状态不再是 committing,则终止这些步骤。

    3. 尝试将 transaction 所做的所有未完成更改写入 数据库,并考虑 transactiondurability hint

    4. 如果将更改写入 数据库时发生错误,则对 transaction 及与错误类型相应的类型(例如 QuotaExceededError 或 "UnknownError" DOMException)执行 中止事务,并终止这些步骤。

    5. 排队一个数据库任务以运行以下步骤:

      1. 如果 transaction升级事务,则将 transaction连接所关联的 数据库升级事务设为 null。

      2. transaction状态设置为 finished

      3. 触发一个事件 名为 completetransaction

        注意: 即使该事件的某个事件处理程序抛出了异常,事务仍会被提交,因为将数据库更改写入发生在该事件触发之前。只有在事务已成功写入之后,才会触发 complete 事件。

      4. 如果 transaction升级事务,则令 request 为与 transaction 关联的 请求,并将 requesttransaction 设置为 null。

5.5. 中止事务

要使用要中止的 transactionerror中止事务,请执行以下步骤:

  1. 如果transaction状态已完成,则终止这些步骤。

  2. 事务数据库所做的所有更改都会被撤销。对于升级事务,包括对对象仓库索引集合的更改,以及版本的更改。在事务期间创建的任何对象仓库索引现在在其他算法中被视为已删除。

  3. 如果 transaction升级事务,则使用 transaction 执行 中止升级事务的步骤。

    注意:这会恢复与 transaction 关联的所有 连接对象仓库句柄索引句柄实例的更改。

  4. transaction状态设置为 finished

  5. transactionerror 设置为 error

  6. 遍历transaction请求列表中的每个 request,中止 异步执行请求的步骤,设置 requestprocessed flag 为 true,并排队一个数据库任务执行以下步骤:

    1. requestdone flag 设置为 true。

    2. requestresult 设置为 undefined。

    3. requesterror 设置为新创建的 "AbortError" DOMException

    4. 触发一个事件 名为 errorrequest ,其 bubblescancelable 属性初始化为 true。

    注意:这并不总是会触发 error 事件。例如,如果由于在提交事务时发生错误而中止事务,或这是最后一个失败的请求。

  7. 排队一个数据库任务以执行以下步骤:

    1. 如果 transaction升级事务,则将 transaction连接所关联的 数据库升级事务设为 null。

    2. 触发一个事件 名为 aborttransaction ,其 bubbles 属性初始化为 true。

    3. 如果 transaction升级事务,则:

      1. request 为与 transaction 关联的 open request

      2. requesttransaction 设置为 null。

      3. requestresult 设置为 undefined。

      4. requestprocessed flag 设置为 false。

      5. requestdone flag 设置为 false。

5.6. 异步执行请求

要用 source 对象和待在数据库上执行的 operation 以及可选的 request 异步执行请求,请按以下步骤执行:

如果创建的请求所属的 事务中止,可随时终止本步骤,按中止事务流程处理。

  1. transaction 为与 source 关联的 事务

  2. 断言transaction状态active

  3. 如果未提供 request,则令 request 为一个新的 请求,其 sourcesource

  4. request 添加到 transaction请求列表末尾。

  5. 并行运行以下步骤:

    1. 等待 request 成为 transaction请求列表中第一个未被处理的项。

    2. result 为执行 operation 的结果。

    3. 如果 result 是错误,且 transaction状态committing,则对 transactionresult 执行 中止事务,并终止这些步骤。

    4. 如果 result 是错误,则回滚 operation 所做的所有更改。

      注意:这只会回滚该请求所做的更改,不会影响事务所做的其他更改。

    5. requestprocessed flag 设置为 true。

    6. 排队一个数据库任务以运行以下步骤:

      1. transaction请求列表移除 request

      2. requestdone flag 设置为 true。

      3. 如果 result 是错误,则:

        1. requestresult 设置为 undefined。

        2. requesterror 设置为 result

        3. request 执行 触发错误事件

      4. 否则:

        1. requestresult 设置为 result

        2. requesterror 设置为 undefined。

        3. request 执行 触发成功事件

  6. 返回 request

5.7. 升级数据库

要用 connection(一个连接)、新 versionrequest 升级数据库,请按以下步骤执行:

  1. dbconnection数据库

  2. transaction 为一个新的 升级事务,并使用 connection 作为 连接

  3. transaction范围 设置为 connection对象仓库集合

  4. db升级事务设置为 transaction

  5. transaction状态设置为 inactive

  6. 启动 transaction

    注意: 在该 事务完成之前,不能打开其他到同一个 连接数据库

  7. old versiondb版本

  8. db版本设置为 version。此更改视为 事务的一部分,因此如果事务被中止,此更改将被回滚。

  9. requestprocessed flag 设置为 true。

  10. 排队一个数据库任务以执行以下步骤:

    1. requestresult 设置为 connection

    2. requesttransaction 设置为 transaction

    3. requestdone flag 设置为 true。

    4. transaction状态 设置为 active

    5. didThrow触发 version change 事件(事件名为 upgradeneeded,目标为 request,参数为 old versionversion)的结果。

    6. 如果 transaction状态active,则:

      1. transaction状态设置为 inactive

      2. 如果 didThrow 为 true,则对 transaction 和新创建的 "AbortError" DOMException执行 中止事务

  11. 等待 transaction 完成

    注意:事务生命周期内调用的一些算法(如提交事务中止事务的步骤)包含升级事务专有的步骤。

5.8. 中止升级事务

要用 transaction 中止升级事务,请按以下步骤执行:

注意: 此步骤在中止事务流程中按需运行,会回滚对数据库的变更,包括关联的对象存储区索引集合,以及版本变更。

  1. connectiontransaction连接

  2. databaseconnection数据库

  3. database 之前存在,设 connection版本database版本;若新创建则为 0。

    注意: 此操作会回滚 version 属性在 IDBDatabase 对象上的值。

  4. database 之前存在,设 connection对象存储区集合database 中的对象存储区集合;若新创建则设为空集合。

    注意: 此操作会回滚 objectStoreNamesIDBDatabase 对象上的值。

  5. 遍历与 transaction 关联的每个对象存储区句柄 handle,包括事务期间新建或删除的对象存储区的句柄:

    1. handle对象存储区不是本事务新建的,则设 handle名称为其对象存储区名称

    2. handle索引集合为引用其对象存储区索引集合。

    注意: 此操作会回滚 nameindexNames 在相关 IDBObjectStore 对象上的值。

    此操作如何可观测? 虽然脚本无法在事务中止后通过 objectStore() 方法访问对象存储区,但仍可持有 IDBObjectStore 实例引用,并可查询 nameindexNames 属性。
  6. 遍历与 transaction 关联的每个索引句柄 handle,包括事务期间新建或删除的索引的句柄:

    1. handle索引不是本事务新建的,则设 handle名称为其索引名称

    注意: 此操作会回滚 name 在相关 IDBIndex 对象上的值。

    此操作如何可观测? 虽然脚本无法在事务中止后通过 index() 方法访问索引,但仍可持有 IDBIndex 实例引用,并可查询 name 属性。

注意: 即使被中止的 升级事务正在创建一个新的 数据库IDBDatabase 实例的 name 属性不会被修改。

5.9. 触发成功事件

要在 request触发成功事件,请按以下步骤执行:

  1. event 为用 Event 创建事件的结果。

  2. eventtype 属性为 "success"。

  3. eventbubblescancelable 属性为 false。

  4. transactionrequest事务

  5. legacyOutputDidListenersThrowFlag 初始为 false。

  6. 如果 transaction状态inactive, 则将 transaction状态设置为 active

  7. 使用 legacyOutputDidListenersThrowFlag,在 request分发 event

  8. 如果 transaction状态active,则:

    1. transaction状态设置为 inactive

    2. 如果 legacyOutputDidListenersThrowFlag 为 true,则对 transaction 和新创建的 "AbortError" DOMException执行 中止事务

    3. 如果 transaction请求列表为空,则对 transaction 执行 提交事务

5.10. 触发错误事件

要在 request触发错误事件,请执行以下步骤:

  1. event 为使用 Event 创建事件的结果。

  2. eventtype 属性设置为 "error"。

  3. eventbubblescancelable 属性设置为 true。

  4. transactionrequest事务

  5. legacyOutputDidListenersThrowFlag 初始值为 false。

  6. 如果 transaction状态inactive,则将 transaction状态设置为 active

  7. 使用 legacyOutputDidListenersThrowFlag,在 request分发 event

  8. 如果 transaction状态active,则:

    1. transaction状态设置为 inactive

    2. 如果 legacyOutputDidListenersThrowFlag 为 true,则对 transaction 和新创建的 "AbortError" DOMException执行 中止事务,并终止这些步骤。即使 eventcanceled flag 为 false,也会执行此操作。

      注意: 这意味着如果错误事件被触发且事件处理器抛出异常,transactionerror 属性将被设置为一个 AbortError,而不是 requesterror,即使 preventDefault() 从未被调用。

    3. 如果 eventcanceled flag 为 false,则对 transactionrequesterror 执行 中止事务,并终止这些步骤。

    4. 如果 transaction请求列表为空,则对 transaction 执行 提交事务

5.11. 克隆值

要在 transaction 期间于 targetRealm 中对 value 进行克隆,请执行以下步骤:

  1. 断言transaction状态active

  2. transaction状态设置为 inactive

    注意: 之所以将 事务设为 inactive,是为了防止克隆操作触发的 getter 或其他副作用对该事务发起额外请求。

  3. serialized? StructuredSerializeForStorage(value) 的结果。

  4. clone? StructuredDeserialize(serialized, targetRealm) 的结果。

  5. transaction状态设置为 active

  6. 返回 clone

5.12. 创建用于检索多个项的请求

要从 对象存储区索引targetRealmsourceHandlekindqueryOrOptions 和可选的 count 创建用于检索多个项的请求,请按以下步骤执行:

  1. sourcesourceHandle索引对象存储区。 若 sourceHandle索引句柄,则 source该句柄关联的索引; 否则 source该对象存储区句柄关联的对象存储区

  2. 如果 source 已被删除,抛出 "InvalidStateError" DOMException

  3. 如果 source索引且其对象存储区已被删除,抛出 "InvalidStateError" DOMException

  4. transactionsourceHandle事务

  5. 如果 transaction状态不是激活抛出 "TransactionInactiveError" DOMException

  6. range键范围

  7. direction游标方向

  8. 如果用 queryOrOptions 运行 是否为潜在有效键范围 返回 true,则:

    1. queryOrOptions 运行 转换为键范围,设为 range。异常重新抛出。

    2. direction 为 "next"。

  9. 否则:

    1. queryOrOptions["query"] 运行 转换为键范围,设为 range。异常重新抛出。

    2. countqueryOrOptions["count"]。

    3. directionqueryOrOptions["direction"]。

  10. operation 为待运行的算法。

  11. 如果 source索引,则设 operation 为用 targetRealmsourcerangekinddirection 和(如给定)count运行 从索引检索多个项

  12. 否则设 operation 为用 targetRealmsourcerangekinddirection 和(如给定)count运行 从对象存储区检索多个项

  13. 返回用 sourceHandleoperation 运行 异步执行请求的结果(IDBRequest)。

注意: range 可以是键范围IDBKeyRange),用于标识要检索的记录项。如果为 null 或未指定,则使用无限键范围。 如果指定了 count,且区间内记录数超过 count,则只检索前 count 项。

6. 数据库操作

本节描述在数据库中的对象存储区索引上进行的各种操作。 这些操作由异步执行请求步骤运行。

注意: 下述操作步骤中的 StructuredDeserialize() 调用不会抛出异常(如 ! 前缀所示),因为它们只操作 StructuredSerializeForStorage() 的先前输出结果。

6.1. 对象存储区存储操作

要用 storevalue、可选 keyno-overwrite flag 存储记录到对象存储区,请按以下步骤执行:

  1. 如果 store 使用键生成器

    1. 如果 key 未定义:

      1. key 为用 store 生成键的结果。

      2. 如果 key 失败,则本操作失败,错误为 "ConstraintError" DOMException。算法终止。

      3. 如果 store 也使用内嵌键,则用 valuekeystore键路径运行 通过键路径注入键到值

    2. 否则,对 storekey运行 可能更新键生成器

  2. 如果本步骤给定了 no-overwrite flag 且为 true,且 store 已存在键为 key记录,则操作失败,错误为 "ConstraintError" DOMException。算法终止。

  3. 如果 store 已存在键为 key记录,则用 key 运行 从对象存储区删除记录

  4. store 存储一个记录,键为 key,值为 !StructuredSerializeForStorage(value)。记录存储在对象存储区的记录列表中,按键升序排序。

  5. 对于每个引用 storeindex

    1. index key 为用 valueindex键路径multiEntry 标志运行 通过键路径从值提取键的结果。

    2. 如果 index key 为异常、无效或失败,则不再处理该 index,继续下一个索引。

      注意: 本步骤异常不会重新抛出。

    3. 如果 indexmultiEntry 标志为 false,或 index key 不是数组键,且 index 已存在键为 index key记录,且 index唯一标志为 true,则操作失败,错误为 "ConstraintError" DOMException。算法终止。

    4. 如果 indexmultiEntry 标志为 true 且 index key数组键,且 index 已存在键等于任一子键记录,且 index唯一标志为 true,则操作失败,错误为 "ConstraintError" DOMException。算法终止。

    5. 如果 indexmultiEntry 标志为 false,或 index key 不是数组键,则在 index 存储一个记录,键为 index key,值为 key。记录存储在 index记录列表中,主排序为记录键,次排序为记录值,均为升序

    6. 如果 indexmultiEntry 标志为 true 且 index key数组键,则遍历 index key 的每个子键,在 index 存储一个记录,键为 subkey,值为 key。记录存储在 index记录列表中,主排序为记录键,次排序为记录值,均为升序

      注意: 子键可以为空,此时不会向索引添加记录。

      注意: 即使子键中有成员本身是数组键,也直接用该成员作为索引记录的键,不会展开或“解包”生成多行,只处理最外层数组键

  6. 返回 key

6.2. 对象存储区检索操作

要用 targetRealmstorerange 从对象存储区检索值,请按以下步骤执行。返回 undefined、ECMAScript 值或错误(DOMException):

  1. recordstore记录列表中第一个其range 内的记录(如有)。

  2. 如果未找到 record,返回 undefined。

  3. serializedrecord。如底层存储读取该值时出错,返回新创建的 "NotReadableError" DOMException

  4. 返回 ! StructuredDeserialize(serialized, targetRealm)。

要用 storerange 从对象存储区检索键,请按以下步骤执行:

  1. recordstore记录列表中第一个其range 内的记录(如有)。

  2. 如果未找到 record,返回 undefined。

  3. 返回用 record 的键运行 转换键为值的结果。

要用 targetRealmstorerangekinddirection 和可选 count 从对象存储区检索多个项,请按以下步骤执行:

  1. 如果未给定 count 或为 0,则令 count 为无穷大。

  2. records 为一个空的记录列表

  3. 如果 direction 为 "next" 或 "nextunique", 设 recordsstore记录列表中前 count 个其range 内的记录。

  4. 如果 direction 为 "prev" 或 "prevunique", 设 recordsstore记录列表中最后 count 个其range 内的记录。

  5. list 为一个空列表

  6. 遍历 records 中的每个 record,根据 kind 分支:

    "key"
    1. key 为用 record 的键运行 转换键为值的结果。

    2. key添加到list

    "value"
    1. serializedrecord

    2. value! StructuredDeserialize(serialized, targetRealm)。

    3. value添加到list

    "record"
    1. keyrecord 的键。

    2. serializedrecord

    3. value! StructuredDeserialize(serialized, targetRealm)。

    4. recordSnapshot 为新建的记录快照,其设为 key设为 value主键设为 key

    5. recordSnapshot 添加到 list

  7. 返回 list

6.3. 索引检索操作

要用 targetRealmindexrange 从索引检索引用值,请按以下步骤执行:

  1. recordindex记录列表中第一个其range 内的记录(如有)。

  2. 如果未找到 record,返回 undefined。

  3. serializedrecord引用值

  4. 返回 ! StructuredDeserialize(serialized, targetRealm)。

要用 indexrange 从索引检索值,请按以下步骤执行:

  1. recordindex记录列表中第一个其range 内的记录(如有)。

  2. 如果未找到 record,返回 undefined。

  3. 返回用 record运行 转换键为值的结果。

要用 targetRealmindexrangekinddirection 和可选 count 从索引检索多个项,请按以下步骤执行:

  1. 如果未给定 count 或为 0,则令 count 为无穷大。

  2. records 为一个空记录列表

  3. 根据 direction 分支:

    "next"
    1. recordsindex记录列表中前 count 个其range 内的记录。

    "nextunique"
    1. rangeRecords 为包含 index记录列表中键在 range 内的记录的列表。

    2. rangeRecordsLengthrangeRecords长度

    3. i 为 0。

    4. i 小于 rangeRecordsLength 时:

      1. 增加 i

      2. 如果 record长度等于 count跳出

      3. 如果用 |rangeRecords[i]| 和 |rangeRecords[i-1]| 的键运行 比较两个键结果相同,继续

      4. 否则追加 |rangeRecords[i]| 到 records

    "prev"
    1. recordsindex记录列表中最后 count 个其range 内的记录。

    "prevunique"
    1. rangeRecords 为包含 index记录列表中键在 range 内的记录的列表。

    2. rangeRecordsLengthrangeRecords长度

    3. i 为 0。

    4. i 小于 rangeRecordsLength 时:

      1. 增加 i

      2. 如果 record长度等于 count跳出

      3. 如果用 |rangeRecords[i]| 和 |rangeRecords[i-1]| 的键运行 比较两个键结果相同,继续

      4. 否则前置 |rangeRecords[i]| 到 records

  4. list 为一个空列表

  5. 遍历 records 中的每个 record,根据 kind 分支:

    "key"
    1. key 为用 record 的值运行 转换键为值的结果。

    2. key 添加到 list

    "value"
    1. serializedrecord引用值

    2. value! StructuredDeserialize(serialized, targetRealm)。

    3. value 添加到 list

    "record"
    1. index keyrecord 的键。

    2. keyrecord 的值。

    3. serializedrecord引用值

    4. value! StructuredDeserialize(serialized, targetRealm)。

    5. recordSnapshot 为新建的记录快照,其设为 index key设为 value主键设为 key

    6. recordSnapshot 添加到 list

  6. 返回 list

6.4. 对象存储区删除操作

要用 storerange 从对象存储区删除记录,请按以下步骤执行:

  1. store记录列表中移除所有键在 range 内的记录(如有)。

  2. 对于每个引用 storeindex,移除 index记录列表中值在 range 内的所有记录(如有)。

  3. 返回 undefined。

6.5. 记录计数操作

要用 sourcerange 统计区间内的记录数,请按以下步骤执行:

  1. countsource 的记录列表中键在 range 内的记录数量(如有)。

  2. 返回 count

6.6. 对象存储区清空操作

要用 store 清空对象存储区,请按以下步骤执行:

  1. 移除 store 的所有记录。

  2. 在所有引用 store索引中,移除所有记录

  3. 返回 undefined。

6.7. 游标迭代操作

要用 targetRealmcursor,可选 keyprimaryKey 以及可选 count 迭代游标,请按以下步骤执行:

  1. sourcecursor

  2. directioncursor方向

  3. 断言:如给定 primaryKey,则 source索引direction 为 "next" 或 "prev"。

  4. recordssource 的记录列表。

    注意: records 总是按升序排序。 如 source索引,则 records 会按升序做二级排序 (索引中的值为引用对象存储区记录的键)。

  5. rangecursor区间

  6. positioncursor位置

  7. object store positioncursor对象存储区位置

  8. 如未给定 count,令 count 为 1。

  9. count 大于 0 时:

    1. 根据 direction 分支:

      "next"

      found recordrecords 中第一个满足所有以下条件的记录:

      • key 已定义:

        • 记录键大于或等于 key

      • primaryKey 已定义:

        • 记录键等于 key 且记录值大于或等于 primaryKey

        • 记录键大于 key

      • position 已定义且 source 为对象存储区:

        • 记录键大于 position

      • position 已定义且 source 为索引:

        • 记录键等于 position 且记录值大于 object store position

        • 记录键大于 position

      • 记录键在 range 内。

      "nextunique"

      found recordrecords 中第一个满足所有以下条件的记录:

      • key 已定义:

        • 记录键大于或等于 key

      • position 已定义:

        • 记录键大于 position

      • 记录键在 range 内。

      "prev"

      found recordrecords 中最后一个满足所有以下条件的记录:

      • key 已定义:

        • 记录键小于或等于 key

      • primaryKey 已定义:

        • 记录键等于 key 且记录值小于或等于 primaryKey

        • 记录键小于 key

      • position 已定义且 source 为对象存储区:

        • 记录键小于 position

      • position 已定义且 source 为索引:

        • 记录键等于 position 且记录值小于 object store position

        • 记录键小于 position

      • 记录键在 range 内。

      "prevunique"

      temp recordrecords 中最后一个满足所有以下条件的记录:

      • key 已定义:

        • 记录键小于或等于 key

      • position 已定义:

        • 记录键小于 position

      • 记录键在 range 内。

      temp record 定义,则令 found recordrecords 中第一个键等于 temp record 键的记录。

      注意: 用 "prevunique" 迭代访问的记录与 "nextunique" 访问的记录相同,只是顺序相反。

    2. found record 未定义:

      1. cursor为 undefined。

      2. source 为索引,设 cursor对象存储区位置为 undefined。

      3. cursor仅键标志为 false,设 cursor为 undefined。

      4. 返回 null。

    3. positionfound record 的键。

    4. source 为索引,令 object store positionfound record 的值。

    5. 减少 count

  10. cursor位置position

  11. source 为索引,设 cursor对象存储区位置object store position

  12. cursorfound record 的键。

  13. cursor仅键标志为 false:

    1. serializedfound record(如 source 为对象存储区),或 found record引用值(否则)。

    2. cursor! StructuredDeserialize(serialized, targetRealm)

  14. cursor已获取值标志为 true。

  15. 返回 cursor

7. ECMAScript 绑定

本节定义了本规范中值如何与 ECMAScript 值相互转换,以及如何通过键路径从 ECMAScript 值中提取或注入键。本节引用了 ECMAScript 语言规范中的类型和算法,并采用了一些算法约定。 [ECMA-262] 未在此详细说明的转换由 [WEBIDL] 定义。

7.1. 从值中提取键

要用 valuekeyPath 和可选 multiEntry flag 通过键路径从值中提取键,请按以下步骤执行。结果可能为、invalid、failure,或步骤可能抛出异常。

  1. r 为用 valuekeyPath运行 在值上求值键路径的结果。异常重新抛出。

  2. r 为 failure,返回 failure。

  3. multiEntry flag 为 false,则令 key 为用 r运行 值转换为键的结果;否则为用 r运行 值转换为多条目键的结果。异常重新抛出。

  4. key 为 "invalid value" 或 "invalid type",返回 invalid。

  5. 返回 key

要用 valuekeyPath 在值上求值键路径,请按以下步骤执行。结果可能为 ECMAScript 值或 failure,或步骤可能抛出异常。

  1. keyPath 是字符串列表:

    1. result 为新建的 Array 对象,创建方法同 [] 表达式。

    2. i 为 0。

    3. 遍历 keyPath 中每个 item

      1. key 为递归用 itemvalue运行 在值上求值键路径的结果。

      2. 断言key 不是异常完成

      3. key 为 failure,终止整体算法并返回 failure。

      4. p! ToString(i)。

      5. statusCreateDataProperty(result, p, key)。

      6. 断言status 为 true。

      7. 增加 i

    4. 返回 result

      注意: 这里最多只会递归一层,因为键路径序列不能嵌套。

  2. keyPath 是空字符串,返回 value 并跳过剩余步骤。

  3. identifiers 为用 keyPath 按 U+002E FULL STOP 字符(.)严格分割的结果。

  4. 遍历 identifiers 中每个 identifier,跳转到下方合适步骤:

    Type(value)为 String 且 identifier为 "length"

    value 为 Number,等于 value 的元素数量。

    valueArrayidentifier 为 "length"

    value! ToLength(! Get(value, "length"))。

    valueBlobidentifier 为 "size"

    value 为 Number,等于 valuesize

    valueBlobidentifier 为 "type"

    value 为 String,等于 valuetype

    valueFileidentifier 为 "name"

    value 为 String,等于 valuename

    valueFileidentifier 为 "lastModified"

    value 为 Number,等于 valuelastModified

    否则
    1. Type(value)不是 Object,返回 failure。

    2. hop! HasOwnProperty(value, identifier)。

    3. hop 为 false,返回 failure。

    4. value! Get(value, identifier)。

    5. value 为 undefined,返回 failure。

  5. 断言value 不是异常完成

  6. 返回 value

注意: 上述步骤之所以可以断言,是因为该算法仅应用于 StructuredDeserialize 的输出值,且只访问“自有”属性。

7.2. 向值中注入键

注意: 本节使用的键路径始终为字符串,绝不是序列,因为无法创建既有键生成器又有为序列的键路径对象存储区

要用 valuekeyPath 检查键能否注入到值中,请按以下步骤执行。结果为 true 或 false。

  1. identifiers 为用 keyPath 按 U+002E FULL STOP 字符(.)严格分割的结果。

  2. 断言identifiers 非空。

  3. 移除 identifiers 的最后一个

  4. 遍历剩余 identifiers 中的每个 identifier(如有):

    1. 如果 value 不是 ObjectArray,返回 false。

    2. hop! HasOwnProperty(value, identifier)。

    3. hop 为 false,返回 true。

    4. value! Get(value, identifier)。

  5. valueObjectArray,返回 true,否则返回 false。

注意: 上述步骤可断言,因为该算法只应用于 StructuredDeserialize 的输出值。

要用 valuekeykeyPath 通过键路径注入键到值,请按以下步骤执行:

  1. identifiers 为用 keyPath 按 U+002E FULL STOP 字符(.)严格分割的结果。

  2. 断言identifiers 非空。

  3. lastidentifiers 的最后一个,并将其从列表中移除。

  4. 遍历剩余 identifiers 中的每个 identifier

    1. 断言valueObjectArray

    2. hop! HasOwnProperty(value, identifier)。

    3. hop 为 false:

      1. o 为新建的 Object,创建方法同 ({}) 表达式。

      2. statusCreateDataProperty(value, identifier, o)。

      3. 断言status 为 true。

    4. value! Get(value, identifier)。

  5. 断言valueObjectArray

  6. keyValue 为用 key 运行 键转换为值的结果。

  7. statusCreateDataProperty(value, last, keyValue)。

  8. 断言status 为 true。

注意: 上述步骤可断言,因为该算法只应用于 StructuredDeserialize 的输出值,且已运行 检查键能否注入到值中流程。

7.3. 键转换为值

要用 key 键转换为值,请按以下步骤执行。返回 ECMAScript 值。

  1. typekey类型

  2. valuekey

  3. 根据 type 分支:

    number

    返回与 value 相等的 ECMAScript 数值。

    string

    返回与 value 相等的 ECMAScript 字符串值。

    date
    1. date 为执行 ECMAScript Date 构造函数,参数为 value 的结果。

    2. 断言date 不是异常完成

    3. 返回 date

    binary
    1. lenvalue长度

    2. buffer 为执行 ECMAScript ArrayBuffer 构造函数,参数为 len 的结果。

    3. 断言buffer 不是异常完成

    4. buffer 的 [[ArrayBufferData]] 内部槽条目为 value 的条目。

    5. 返回 buffer

    array
    1. array 为执行 ECMAScript Array 构造函数(无参数)结果。

    2. 断言array 不是异常完成

    3. lenvalue长度

    4. index 为 0。

    5. index 小于 len 时:

      1. entry 为用 value[index] 运行 键转换为值的结果。

      2. statusCreateDataProperty(array, index, entry)。

      3. 断言status 为 true。

      4. 增加 index

    6. 返回 array

7.4. 值转换为键

要用 ECMAScript 值 input 和可选的集合 seen 值转换为键,请按以下步骤执行。 结果可能为、“invalid value”或“invalid type”,或步骤可能抛出异常。

  1. 如果未给定 seen,则令 seen 为新建的空集合

  2. 如果 seen 包含 input,则返回 "invalid value"。

  3. 跳转到下方合适步骤:

    Type(input) 为 Number
    1. input 为 NaN,则返回 "invalid value"。

    2. 否则,返回新建的,其类型为 number,input

    inputDate(有 [[DateValue]] 内部槽)
    1. msinput 的 [[DateValue]] 内部槽的值。

    2. ms 为 NaN,则返回 "invalid value"。

    3. 否则,返回新建的,其类型为 date,ms

    Type(input) 为 String
    1. 返回新建的,其类型为 string,input

    input缓冲区源类型
    1. input 已分离,则返回 "invalid value"。

    2. bytes 为用 input 获取缓冲区源字节副本的结果。

    3. 返回新建的,其类型为 binary,bytes

    input数组异对象
    1. len? ToLength( ? Get(input, "length"))。

    2. input 添加到 seen

    3. keys 为新建的空列表。

    4. index 为 0。

    5. index 小于 len 时:

      1. hop? HasOwnProperty(input, index)。

      2. hop 为 false,返回 "invalid value"。

      3. entry? Get(input, index)。

      4. key 为用参数 entryseen 运行 值转换为键的结果。

      5. ReturnIfAbrupt(key)。

      6. key 为 "invalid value" 或 "invalid type",终止本步骤并返回 "invalid value"。

      7. key 添加到 keys

      8. 增加 index

    6. 返回新建的数组键keys

    否则

    返回 "invalid type"。

要用 ECMAScript 值 input 值转换为多条目键,请按以下步骤执行。 结果可能为、“invalid value”或“invalid type”,或步骤可能抛出异常。

  1. input数组异对象

    1. len? ToLength( ? Get(input, "length"))。

    2. seen 为仅包含 input 的新集合

    3. keys 为新建的空列表

    4. index 为 0。

    5. index 小于 len 时:

      1. entryGet(input, index)。

      2. entry 不是异常完成

        1. key 为用 entryseen 运行 值转换为键的结果。

        2. key 不是 "invalid value"、"invalid type" 或异常完成,且 keys 中没有key 相等,则 key 添加到 keys

      3. 增加 index

    6. 返回新建的数组键keys

  2. 否则,返回用参数 input 运行 值转换为键的结果。异常重新抛出。

注意: 这些步骤类似于值转换为键, 但如果顶层值为 Array, 则无法转换为键的成员会被忽略,且会去除重复项。

例如,值 [10, 20, null, 30, 20] 会被转换为数组键,其子键为 10、20、30。

8. 隐私注意事项

本节为非规范性内容。

8.1. 用户追踪

第三方主机(或任何能够将内容分发到多个站点的对象)可以利用其客户端数据库中存储的唯一标识符,在多个会话间追踪用户,建立用户活动档案。结合某些了解用户真实身份的站点(如需要认证凭据的电商网站),这可能让压制性组织比纯匿名 Web 使用环境下更精准地定位个人。

有多种技术可用于降低用户追踪的风险:

阻止第三方存储

用户代理可以限制数据库对象的访问,仅允许顶层文档所属域的脚本访问,例如拒绝其它域在 iframe 页面中对 API 的访问。

让存储的数据过期

用户代理可在一段时间后自动删除已存储的数据。

这可以限制站点追踪用户的能力,因为站点只能在用户自己认证(如购买或登录服务)时跨多个会话追踪用户。

但这也使用户数据面临丢失风险。

将持久存储视为 Cookie

用户代理应以与 HTTP 会话 Cookie 强关联的方式向用户展示数据库功能。[COOKIES]

这可能促使用户用合理怀疑态度看待此类存储。

按站点对数据库访问进行白名单

用户代理可要求用户授权后,站点方能访问数据库。

第三方存储归属信息

用户代理可记录导致数据被存储的内容来自的第三方的站点

如果这些信息用于呈现当前持久存储中的数据视图,用户可以据此做出明智决定,选择要清理哪些存储内容。结合黑名单(“删除此数据并阻止该域名再次存储数据”),用户可将持久存储仅限于信任的站点。

共享黑名单

用户代理可允许用户共享其持久存储域名黑名单。

这样社区可以协作保护隐私。

虽然上述建议可以防止此 API 被轻易用于用户追踪,但并不能完全阻止。在单一域名下,站点仍可在会话期间追踪用户,并将所有信息与任何识别信息(姓名、信用卡号、地址)一起传给第三方。如果第三方与多个站点合作获取这些信息,仍可建立用户档案。

然而,即使用户代理完全不配合,也可以一定程度上追踪用户,比如使用 URL 中的会话标识符,这一技术已被广泛用于无害目的,但同样可轻易用于用户追踪(甚至事后追踪)。这些信息可与其它站点共享,利用访客的 IP 地址及其它用户特定数据(如 user-agent 头和配置设置)把不同会话归为同一用户档案。

如果持久存储的用户界面将本规范描述的持久存储功能与 HTTP 会话 Cookie 的数据分开展示,用户很可能只删除其中一处的数据。这将让站点把两项功能当作彼此的冗余备份,从而破坏用户保护隐私的努力。

8.3. 数据敏感性

用户代理应将持久存储的数据视为潜在敏感数据;此机制完全可能存储电子邮件、日历、健康记录或其它机密文档。

为此,用户代理应确保删除数据时,数据能被及时从底层存储中清除。

9. 安全注意事项

9.1. DNS 欺骗攻击

由于存在 DNS 欺骗攻击的可能,无法保证宣称属于某域名的主机确实来自该域。为降低这种风险,页面可使用 TLS。采用 TLS 的页面可确保只有使用 TLS 且证书标识为同一域的页面能访问其数据库。

9.2. 跨目录攻击

共享同一主机名的不同作者(如在 geocities.com 上托管内容的用户)都共享一套数据库。

没有按路径限制访问的功能。共享主机上的作者因此应避免使用这些功能,因为其它作者可以轻易读取或覆盖数据。

注意: 即使提供了路径限制功能,常规 DOM 脚本安全模型也能轻易绕过此保护,从任何路径访问数据。

9.3. 实现风险

实现这些持久存储功能的主要风险是让恶意站点读取其它域的数据,以及让恶意站点写入数据后被其它域读取。

让第三方站点读取本不应被其域访问的数据会造成信息泄露,例如某域上的用户购物心愿单可被其它域用于定向广告,或某文档编辑站点存储的用户机密文档被竞争公司站点查看。

让第三方站点向其它域的持久存储写入数据会导致信息伪造,同样危险。例如恶意站点可往用户心愿单添加记录,或将用户会话标识设置为恶意站点已知的 ID,以追踪用户在受害站点上的操作。

因此,严格遵循本规范描述的存储键分区模型对于用户安全至关重要。

如果用主机名或数据库名生成持久化到文件系统的路径,则必须进行适当的转义,以防攻击者用相对路径如 "../" 访问其它存储键的信息。

9.4. 持久化风险

实际实现会将数据持久化到非易失性存储介质。数据存储时会被序列化,读取时反序列化,具体序列化格式由用户代理决定。用户代理可能会随时间更改其序列化格式,例如为支持新数据类型或提升性能。为满足本规范的操作要求,实现必须能以某种方式处理旧序列化格式。不正确处理旧数据会导致安全问题。除了基础序列化问题,序列化数据还可能包含在新版用户代理中无效的假设。

一个实际例子是 RegExp 类型。 StructuredSerializeForStorage 操作支持序列化 RegExp 对象。典型的用户代理会将正则表达式编译为机器指令,并假设输入数据的传递和结果的返回方式。如果这些内部状态作为数据存储的一部分被序列化,日后反序列化时可能出现各种问题。例如,数据的传递方式可能已变动。编译器输出中的安全漏洞可能已在用户代理更新中修复,但在序列化的内部状态中仍然存在。

用户代理必须正确识别和处理旧数据。一种做法是在序列化格式中加入版本标识符,在遇到旧数据时根据脚本可见状态重建任何内部状态。

10. 可访问性注意事项

本节为非规范性内容。

本规范描述的 API 在可访问性方面考虑有限:

该 API 允许存储结构化内容。文本内容可作为字符串存储。API 支持开发者存储如图片或音频等非文本内容,如 BlobFile、 或 ImageData 对象。使用 API 创建动态内容应用的开发者应确保内容能被不同技术和需求的用户访问。

虽然 API 本身未定义具体机制,但结构化内容的存储也允许开发者存储国际化内容,通过不同记录或记录内结构保存语言版本。

API 未定义或要求用户代理生成用于与 API 交互的用户界面。用户代理可选提供支持 API 的界面元素。例如,当需要额外存储配额时弹窗提示用户、展示特定网站使用的存储量,或提供针对 API 存储的工具如检查、修改或删除记录。任何此类界面元素都必须考虑辅助工具。例如,以图形方式展示存储配额使用比例的界面,也必须向屏幕阅读器等工具提供同样数据。

11. 修订历史

本节为非规范性内容。

以下是自本规范上次发布以来的变更摘要。完整修订历史可在此处查阅。 第一版修订历史见该文档修订历史。 第二版修订历史见该文档修订历史

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, 所有上述人员的反馈和建议都推动了本规范的改进。

一致性

文档约定

一致性要求通过描述性断言和 RFC 2119 术语的结合来表达。 关键字“MUST”(必须)、“MUST NOT”(不得)、“REQUIRED”(要求)、“SHALL”(应)、“SHALL NOT”(不应)、“SHOULD”(应该)、“SHOULD NOT”(不应该)、“RECOMMENDED”(推荐)、“MAY”(可以)和“OPTIONAL”(可选) 在本文件的规范性部分中应按照 RFC 2119 说明进行解释。 但为了便于阅读,这些词在本规范中并非全部以大写字母出现。

本规范的所有文本均为规范性内容,除非明确标记为非规范性部分、示例或注释。[RFC2119]

本规范中的示例以“例如”引入,或者通过 class="example" 与规范性文本区分开来,如下所示:

这是一个信息性示例。

信息性注释以“注意”开头,并通过 class="note" 与规范性文本区分开来,如下所示:

注意,这是一个信息性注释。

符合规范的算法

作为算法一部分的命令式要求(如“去除所有前导空格字符”或“返回 false 并中止这些步骤”)应根据引入算法时所用的关键字(如“must”、 “should”、“may”等)进行解释。

以算法或具体步骤表述的一致性要求可以通过任何方式实现,只要最终结果是等效的即可。 特别地,本规范中定义的算法旨在便于理解,并不一定追求性能。 鼓励实现者进行优化。

索引

本规范定义的术语

引用定义的术语

参考文献

规范性引用

[DOM]
Anne van Kesteren. DOM 标准. 实时标准. URL: https://dom.spec.whatwg.org/
[ECMA-262]
ECMAScript 语言规范. URL: https://tc39.es/ecma262/multipage/
[FileAPI]
Marijn Kruisselbrink. 文件 API. 2024年12月4日. WD. URL: https://www.w3.org/TR/FileAPI/
[HTML]
Anne van Kesteren; 等. HTML 标准. 实时标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 实时标准. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. 用于 RFCs 表示需求级别的关键词. 1997年3月. 最佳当前实践. URL: https://datatracker.ietf.org/doc/html/rfc2119
[STORAGE]
Anne van Kesteren. 存储标准. 实时标准. URL: https://storage.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 实时标准. URL: https://webidl.spec.whatwg.org/

信息性引用

[Charmod-Norm]
Addison Phillips; 等. 万维网字符模型:字符串匹配. 2021年8月11日. NOTE. URL: https://www.w3.org/TR/charmod-norm/
[COOKIES]
A. Barth. HTTP 状态管理机制. 2011年4月. 标准草案. URL: https://httpwg.org/specs/rfc6265.html
[WEBSTORAGE]
Ian Hickson. Web 存储(第二版). 2021年1月28日. REC. URL: https://www.w3.org/TR/webstorage/

IDL 索引

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

[Exposed=(Window,Worker)]
interface IDBOpenDBRequest : IDBRequest {
  // Event handlers:
  attribute EventHandler onblocked;
  attribute EventHandler onupgradeneeded;
};

[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 oldVersion = 0;
  unsigned long long? newVersion = null;
};

partial interface mixin WindowOrWorkerGlobalScope {
  [SameObject] readonly attribute IDBFactory 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;
};

[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 mode = "readonly",
                                         optional IDBTransactionOptions options = {});
  undefined close();

  [NewObject] IDBObjectStore createObjectStore(
    DOMString name,
    optional IDBObjectStoreParameters options = {});
  undefined deleteObjectStore(DOMString name);

  // Event handlers:
  attribute EventHandler onabort;
  attribute EventHandler onclose;
  attribute EventHandler onerror;
  attribute EventHandler onversionchange;
};

enum IDBTransactionDurability { "default", "strict", "relaxed" };

dictionary IDBTransactionOptions {
  IDBTransactionDurability durability = "default";
};

dictionary IDBObjectStoreParameters {
  (DOMString or sequence<DOMString>)? keyPath = null;
  boolean autoIncrement = false;
};

[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 direction = "next");
  [NewObject] IDBRequest openKeyCursor(optional any query,
                                       optional IDBCursorDirection direction = "next");

  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 direction = "next";
};

[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 direction = "next");
  [NewObject] IDBRequest openKeyCursor(optional any query,
                                       optional IDBCursorDirection direction = "next");
};

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

[Exposed=(Window,Worker)]
interface IDBRecord {
  readonly attribute any key;
  readonly attribute any primaryKey;
  readonly attribute any value;
};

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

[Exposed=(Window,Worker)]
interface IDBCursorWithValue : IDBCursor {
  readonly attribute any value;
};

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

MDN

IDBCursor/advance

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/continue

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/continuePrimaryKey

In all current engines.

Firefox10+Safari10.1+Chrome58+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android51+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/delete

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/direction

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/key

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/primaryKey

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/request

In all current engines.

Firefox77+Safari15+Chrome76+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/source

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor/update

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursor

In all current engines.

Firefox16+Safari8+Chrome24+
Opera44+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile43+
MDN

IDBCursorWithValue/value

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBCursorWithValue

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBDatabase/close

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/close_event

In all current engines.

Firefox50+Safari10.1+Chrome30+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile?
MDN

IDBDatabase/close_event

In all current engines.

Firefox50+Safari10.1+Chrome30+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView37+Samsung Internet?Opera Mobile?
MDN

IDBDatabase/createObjectStore

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/deleteObjectStore

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/name

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/objectStoreNames

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/transaction

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/version

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/versionchange_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase/versionchange_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBDatabase

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBFactory/cmp

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBFactory/databases

FirefoxNoneSafari14+Chrome72+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBFactory/deleteDatabase

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBFactory/open

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBFactory

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBIndex/count

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/get

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/getAll

In all current engines.

Firefox44+Safari10.1+Chrome48+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/getAllKeys

In all current engines.

Firefox44+Safari10.1+Chrome48+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/getKey

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/keyPath

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/multiEntry

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/name

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/objectStore

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/openCursor

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/openKeyCursor

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex/unique

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBIndex

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBKeyRange/bound_static

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/includes

In all current engines.

Firefox47+Safari10.1+Chrome52+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/lower

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/lowerBound_static

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/lowerOpen

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/only_static

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/upper

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/upperBound_static

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange/upperOpen

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBKeyRange

In all current engines.

Firefox16+Safari8+Chrome24+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBObjectStore/add

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/autoIncrement

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)?IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/clear

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/count

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/createIndex

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/delete

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/deleteIndex

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/get

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/getAll

In all current engines.

Firefox44+Safari10.1+Chrome48+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android48+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/getAllKeys

In all current engines.

Firefox44+Safari10.1+Chrome48+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android48+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/getKey

In all current engines.

Firefox51+Safari10.1+Chrome48+
Opera45+Edge79+
Edge (Legacy)?IENone
Firefox for Android58+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile43+
MDN

IDBObjectStore/index

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/indexNames

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/keyPath

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/name

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/openCursor

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/openKeyCursor

In all current engines.

Firefox44+Safari10.1+Chrome23+
Opera?Edge79+
Edge (Legacy)?IE10+
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/put

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore/transaction

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBObjectStore

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBOpenDBRequest/blocked_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBOpenDBRequest/upgradeneeded_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBOpenDBRequest

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBRequest/error

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/error_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

IDBTransaction/error_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/readyState

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/result

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/source

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/success_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest/transaction

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBRequest

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android25+Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBTransaction/abort

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/abort_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/commit

In all current engines.

Firefox74+Safari15+Chrome76+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/complete_event

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/db

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/durability

FirefoxNoneSafari15+Chrome83+
Opera70+Edge83+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/error

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/mode

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/objectStore

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction/objectStoreNames

In all current engines.

Firefox10+Safari10.1+Chrome48+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBTransaction

In all current engines.

Firefox16+Safari8+Chrome24+
Opera15+Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile14+
MDN

IDBVersionChangeEvent/IDBVersionChangeEvent

In all current engines.

Firefox25+Safari10+Chrome41+
Opera?Edge79+
Edge (Legacy)?IENone
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBVersionChangeEvent/newVersion

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBVersionChangeEvent/oldVersion

In all current engines.

Firefox10+Safari8+Chrome23+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IDBVersionChangeEvent

In all current engines.

Firefox16+Safari8+Chrome24+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView4.4+Samsung Internet?Opera Mobile?
MDN

indexedDB

In all current engines.

Firefox16+Safari8+Chrome24+
Opera?Edge79+
Edge (Legacy)12+IE10+
Firefox for Android22+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?