索引数据库 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
编辑:
(Microsoft)
前任编辑:
Ali Alabbas (原任职于 Microsoft)
Joshua Bell (原任职于 Google)

摘要

本文档定义了用于保存记录的数据库 API,这些记录包含 简单值和分层对象。每条记录由一个键 和某个值组成。此外,数据库还维护其所存储记录的索引。 应用程序开发者可直接使用 API,通过记录的键或使用索引 来查找记录。可以在此 API 之上构建查询语言。索引数据库可以使用 持久化 B 树数据结构实现。

本文档状态

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

本文档由 Web 应用工作组 使用推荐标准 轨道发布为工作草案。 发布为工作草案 并不意味着 W3C 及其成员认可该文档。

这是一份草案文档, 可能随时被其他文档更新、替换 或废弃。 除作为进行中的工作成果外,不宜以其他方式引用本文档。

本文档由一个依照 W3C 专利政策运作的工作组制定。W3C 维护一份公开的专利 披露列表,其中包含与该工作组交付成果相关的所有专利披露;该页面还包含 披露专利的说明。任何实际知晓某项专利,并且该个人 认为其中包含必要权利要求的人, 必须按照W3C 专利政策第 6 节的规定披露相关信息。

本文档受 2023 年 11 月 3 日 W3C 流程文档约束。

这是 Indexed Database API 的第三版。 第一版, 标题简称为“Indexed Database API”, 于 2015 年 1 月 8 日成为 W3C 推荐标准。 第二版, 标题为“Indexed Database API 2.0”, 于 2018 年 1 月 30 日成为 W3C 推荐标准。

Indexed Database API 3.0 旨在取代 Indexed Database API 2.0。

1. 简介

用户代理需要在本地存储大量对象,以 满足 Web 应用程序的离线数据需求。[WEBSTORAGE] 适用于存储键及其对应值的键值对。 然而,它不提供按顺序检索键、对值进行高效 搜索,也不支持为一个键存储重复值。

本规范提供了一个具体的 API,用于执行高级 键值数据管理,这是大多数复杂 查询处理器的核心。它通过使用事务数据库存储 键及其对应的值(每个键对应一个或多个值),并 提供一种以确定顺序遍历键的方式来实现这一点。这通常 通过使用持久化 B 树数据结构来实现, 该结构被认为可高效执行插入、删除以及 按顺序遍历大量数据记录。

以下示例使用该 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. 令 sorted 为使用代码单元小于算法将 names 按升序排序的结果。

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

详细信息 这与由 String 组成的 Array 上的 sort() 方法相匹配。 此排序会比较每个字符串中的 16 位代码单元, 从而产生高效、一致且确定性的 排序顺序。所得列表不会与任何特定的 字母表顺序或词典顺序相匹配,尤其是对于由 代理对表示的码点。

2.1. 数据库

每个存储 键都有一个关联的数据库集合。一个 数据库具有零个或多个用于 保存数据库中所存储数据的对象存储。

一个数据库具有一个名称,用于在 特定存储键中标识它。该名称是一个名称, 并且在数据库的整个生命周期内保持不变。

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

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

一个数据库至多有一个关联的 升级事务, 它要么为 null,要么是一个升级事务,初始值为 null。

2.1.1. 数据库连接

脚本不会直接与数据库 交互。相反, 脚本通过一个连接间接访问数据库。 一个连接对象可用于 操作 该数据库中的对象。它也是 为该数据库获取 事务的唯一方式。

打开一个数据库的操作会创建一个 连接。 在任意给定时刻,一个给定的数据库可以具有 多个连接。

一个连接只能访问与打开该连接的全局作用域的 存储 键关联的数据库。

注意: 这不受 Document 的 domain 发生变化的影响。

一个连接具有一个版本,该版本在 连接创建时设置。它在 连接的整个生命周期内保持不变, 除非升级被中止,在这种 情况下,它会被设置为数据库的先前版本。一旦 连接关闭, 版本便不再 更改。

每个连接都有一个关闭待处理标志,其初始值为 false。

当一个连接最初 创建时,它处于打开 状态。可以通过多种方式关闭该连接。 如果创建连接的执行上下文 被销毁(例如由于用户离开该 页面),则连接会关闭。还可以 使用关闭数据库连接的步骤显式关闭连接。当 连接关闭时,如果其关闭待处理标志尚未设置为 true, 则始终会将其设置为 true。

用户代理可以在异常 情况下关闭一个连接,例如文件系统访问权限丢失、 权限发生变化,或清除存储键的 存储。如果发生这种情况, 用户代理必须使用该连接并将 forced flag 设置为 true,运行关闭数据库连接。

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

一个连接的获取父级算法 返回 null。

如果尝试升级或删除 数据库,则会在打开的 连接上触发一个类型为 versionchange 的事件。这使连接有机会 关闭, 从而允许升级或删除继续进行。

如果连接被异常关闭,则会在该连接上触发一个类型为 close 的事件。

2.2. 对象存储

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

每个数据库都有一个对象 存储集合。对象 存储集合可以更改,但只能使用升级 事务进行更改, 即响应 upgradeneeded 事件。创建 新数据库时,它不包含任何对象存储。

一个对象存储具有一个记录列表,用于保存 对象存储中存储的数据。每条记录由一个 键和一个值组成。该列表按照键 升序排序。一个给定对象 存储中绝不可能存在键相同的多条记录。

一个对象存储具有一个名称,它是一个名称。 在任一时刻,该名称在其所属的 数据库中都是唯一的。

一个对象存储可以具有 一个键路径。如果 对象存储具有键路径,则称其使用内联键。 否则称其使用外联键。

一个对象存储可以具有 一个键生成器。

对象存储可以从以下三个来源之一为一条记录派生一个键:

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

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

  3. 在对象存储中存储一个值时,也可以显式指定 键。

2.2.1. 对象存储句柄

脚本不会直接与对象 存储交互。相反, 在一个事务内, 脚本通过一个 对象存储 句柄间接访问对象存储。

一个对象存储 句柄具有一个关联的对象存储 和一个关联的事务。多个句柄可以 在不同事务中 与同一个对象 存储关联, 但在一个事务内, 与特定对象 存储关联的对象存储句柄 必须只有一个。

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

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

2.3. 值

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

记录的值是由 StructuredSerializeForStorage 操作输出的 Records。

2.4. 键

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

一个键具有一个关联的类型, 它是以下类型之一: 数值、 日期、 字符串、 二进制, 或 数组。

一个键还具有一个关联的值, 它将是: 如果类型为数值或日期,则为一个 unrestricted double; 如果类型为字符串,则为一个 DOMString; 如果类型为二进制,则为一个字节 序列; 如果类型为数组,则为其他键的列表。

可以按照将值转换为键的步骤,将一个 ECMAScript [ECMA-262] 值转换为一个键。

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

尝试将其他 ECMAScript 值转换为键 将失败。

数组键是一个键,其类型为数组。 一个数组键的子键是 该 数组键的值中的项。

要比较两个 键 a 和 b,运行以下步骤:

  1. 令 ta 为 a 的类型 。

  2. 令 tb 为 b 的类型 。

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

    1. 如果 ta 为数组,则返回 1。

    2. 如果 tb 为数组,则返回 -1。

    3. 如果 ta 为二进制,则返回 1。

    4. 如果 tb 为二进制,则返回 -1。

    5. 如果 ta 为字符串,则返回 1。

    6. 如果 tb 为字符串,则返回 -1。

    7. 如果 ta 为日期,则返回 1。

    8. 断言: tb 为日期。

    9. 返回 -1。

  4. 令 va 为 a 的值。

  5. 令 vb 为 b 的值。

  6. 根据 ta 切换:

    数值
    日期
    1. 如果 va 大于 vb,则返回 1。

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

    3. 返回 0。

    字符串
    1. 如果 va 代码 单元小于 vb, 则返回 -1。

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

    3. 返回 0。

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

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

    3. 返回 0。

    数组
    1. 令 length 为 va 的大小和 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 比较两个 键的结果 为 1,则键 a 大于 键 b。

如果使用 a 和 b 比较两个 键的结果 为 -1,则键 a 小于键 b。

如果使用 a 和 b 比较两个键的结果 为 0,则键 a 等于键 b。

注意: 根据上述规则,负无穷是一个键可能具有的 最低值。 数值键小于日期键。 日期键小于字符串键。 字符串键小于二进制键。 二进制键小于数组键。 不存在最高的键值。 这是因为由任意候选最高键 后跟另一个键组成的数组会更大。

注意: 二进制键的成员按无符号字节值 (范围为 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. 事务

事务用于与 数据库中的数据交互。每当 从数据库读取数据或向数据库写入 数据时,都会使用事务。

事务 可在一定程度上防范应用程序和系统 故障。一个事务可用于存储多条数据 记录或有条件地修改某些数据记录。一个 事务 表示一组原子且持久的数据访问 和数据修改操作。

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

一个事务 具有一个作用域,它是事务可以交互的集合形式的对象存储。

注意: 一个事务的作用域保持 固定,除非该事务是一个升级事务。

如果有任何对象存储同时位于 两个事务的作用域中,则这两个事务具有重叠作用域。

一个事务 具有一个模式,用于确定可以对该事务执行哪些类型 的交互。模式 在创建事务时设置,并在事务的整个生命周期内保持 不变。一个事务的模式是以下之一:

"readonly"

该事务只允许读取数据。此类型的事务不能 执行任何修改。其优点是,即使多个只读事务的作用域重叠,也可以同时启动, 即它们 使用相同的 对象存储。数据库打开后,可随时创建 此类型的事务。

"readwrite"

该事务允许读取、修改和删除 现有对象存储中的数据。但是不能添加或删除对象存储和索引。多个 "readwrite" 事务 如果其作用域 重叠,则不能同时启动, 因为这意味着它们可以在事务执行过程中 修改彼此的数据。数据库打开后,可随时 创建此类型的事务。

"versionchange"

该事务允许读取、修改和删除 现有对象存储中的数据,还可以创建和删除对象 存储和索引。它是唯一能够执行 这些操作的事务类型。此类型的事务不能手动创建,而是 在触发 upgradeneeded 事件时自动创建。

一个事务 具有一个持久性提示。这是向用户代理提供的提示,用于说明 在提交事务时应优先考虑性能还是持久性。持久性提示是 以下之一:

"strict"

用户代理只有在确认所有未完成的更改都已成功写入持久性存储 介质后,才可以认为该事务已成功提交。

"relaxed"

一旦所有未完成的更改都已写入操作系统,而无需随后进行验证, 用户代理便可以认为该事务已成功提交。

"default"

用户代理应对存储 桶使用其默认的持久性行为。如果没有另行指定,这是事务的默认值。

注意: 在典型实现中,"strict" 是向用户代理提供的提示,要求其在触发 complete 事件之前刷新所有操作系统 I/O 缓冲区。虽然这样可以提高在之后发生操作系统崩溃或断电时 更改仍能持久保存的可信度,但刷新缓冲区可能需要较长时间,并会消耗 便携设备的电量。

鼓励 Web 应用程序对缓存或快速变化的记录等临时数据使用 "relaxed", 并在降低数据丢失风险的重要性高于对性能和功耗的影响时使用 "strict"。 鼓励实现权衡应用程序提供的持久性提示与其对 用户和设备的影响。

一个事务 可以具有一个清理事件循环, 它是一个事件循环。

一个事务 具有一个包含针对该事务发出的待处理请求的请求列表。

一个事务 具有一个错误,如果该事务被中止,则会设置该错误。

注意: 实现者需要记住,值 "null" 被视为一个错误,因为它会通过 abort() 设置。

一个事务的获取 父级 算法返回该 事务的连接。

只读事务是 一个事务, 其模式为 "readonly"。

读写事务 是一个事务,其 模式为 "readwrite"。

2.7.1. 事务生命周期

一个事务 具有一个状态,它是以下 状态之一:

活动

事务首次被创建时, 以及分派与该事务关联的请求所产生的事件期间,事务处于此状态。

事务处于此状态时,可以针对该事务发出新的 请求。

非活动

事务创建后控制权返回事件 循环时,以及没有正在分派事件时,事务处于此状态。

事务处于此状态时,不能针对该事务发出任何 请求。

正在提交

一旦与事务关联的所有请求都已完成, 事务便会在尝试提交时进入此状态。

事务处于此状态时,不能针对该事务发出任何 请求。

已完成

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

事务处于此状态时,不能针对该事务发出任何 请求。

事务预期应当是短生命周期的。下文所述的 自动 提交功能鼓励采用这种方式。

注意: 作者仍可以使事务长时间保持存活; 但是,不建议采用这种使用模式,因为它可能导致较差的 用户体验。

一个 事务的生命周期如下:

  1. 一个事务以一个作用域和一个 模式创建。事务创建时,其 状态最初为 活动。

  2. 当实现能够强制执行下文定义的有关 事务作用域 和模式的约束时, 实现必须排入一个 数据库任务,以异步启动该 事务。

    一旦事务已启动, 实现便可以开始执行针对该事务发出的请求。 请求必须按照其针对事务发出的顺序执行。 同样,其结果也必须按照针对特定事务发出请求的顺序返回。 不保证不同事务中请求的 结果返回顺序。

    注意: 事务模式可确保 针对不同事务发出的两个 请求可以按任意顺序执行,而不会影响最终存储在 数据库中的数据。

  3. 当与事务关联的每个请求被处理时, 将触发一个 success 或 error 事件。在事件被分派期间,事务 状态会设置为 活动, 从而允许 针对事务发出其他请求。事件分派完成后, 事务的 状态会再次设置为 非活动。

  4. 事务在完成之前的任何时候都可以被中止, 即使该 事务当前并非活动状态或尚未 启动。

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

    事务中止时,实现必须撤销(回滚) 在该事务期间对数据库所做的任何更改。 这既包括对 对象存储内容的更改, 也包括添加和删除对象存储和索引。

  5. 当一个非活动 事务满足以下条件时,实现必须尝试提交 该事务:针对该事务发出的所有请求 均已完成且其返回结果已得到处理,没有 针对该事务发出新请求,并且 该事务尚未被中止。

    显式调用 commit() 将发起一次 提交, 而无需等待脚本处理请求结果。

    提交时,事务的状态会设置为 正在提交。 实现必须以原子方式 写入由针对该事务发出的请求 对数据库所做的任何更改。 也就是说,要么必须写入所有更改, 要么如果发生错误(例如磁盘写入错误), 实现不得将任何更改写入数据库, 并且将执行中止 事务的步骤。

  6. 当事务被提交或中止时, 其状态 会设置为已完成。

实现必须允许在事务处于活动状态时,随时针对该事务发出 请求。即使事务尚未 启动,情况也是如此。在事务 启动之前, 实现不得执行这些 请求;但是,实现必须记录这些 请求及其顺序。

一个事务 从被创建之时起,直到其状态被设置为已完成之前,被称为存活。

要清理 Indexed Database 事务,运行 以下步骤。 如果清理了任何事务,它们将返回 true,否则返回 false。

  1. 如果没有任何事务的清理事件 循环与当前 事件循环匹配,则返回 false。

  2. 对于每个其清理事件 循环 与当前事件 循环匹配的事务 transaction:

    1. 将 transaction 的状态设置为非活动。

    2. 清除 transaction 的清理事件循环。

  3. 返回 true。

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

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

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

2.7.2. 事务调度

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

实现可以施加其他约束。例如,实现不要求并行启动作用域不重叠的 读写事务,也可以 对已启动的事务数量施加限制。

注意: 这些约束意味着以下情况:

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. 打开请求

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

一个打开 请求的源 始终为 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。

对于在将值转换为 键时返回 "invalid value" 的 Date、 Array 和 ArrayBuffer 第一个参数,getAll() 和 getAllKeys() 会抛出异常。 例如,以一个值为 NaN 的 Date 作为第一个参数运行 getAll() 会抛出异常,而不是成功使用一个具有默认值的 IDBGetAllOptions 字典。

2.10. 游标

游标用于沿特定方向迭代 一个索引或一个对象存储中的一系列记录。

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

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

一个游标具有一个位于 一个索引或一个对象存储中的记录范围。

一个游标具有一个源,即来自 游标源 句柄的一个 索引或一个对象存储。 游标的源 表明哪个索引或 对象存储与游标 正在迭代的记录关联。 如果游标的源 句柄是一个索引 句柄, 那么游标的源 是该索引 句柄所 关联的索引。 否则,游标的源 是该对象存储句柄所关联的对象 存储。

一个游标具有一个方向,该方向 决定游标在迭代时是按 记录键单调递增还是递减的顺序 移动,以及在迭代索引时是否跳过重复值。 游标的方向还决定游标的初始位置是在其 源的开头还是末尾。游标的 方向是以下之一:

"next"

此方向使游标在 源的开头打开。迭代时, 游标应当 按键单调递增的顺序产生所有 记录,包括重复记录。

"nextunique"

此方向使游标在 源的开头打开。迭代时, 游标不应 产生具有相同键的记录,但除此之外会按键 单调递增的顺序产生所有记录。对于每个具有 重复值的键,只会产生第一条记录。当 源是一个对象存储,或者是一个 索引且该索引的 唯一 标志设置为 true 时,此方向的行为与 "next" 完全相同。

"prev"

此方向使游标在 源的末尾打开。迭代时, 游标应当 按键单调递减的顺序产生所有 记录,包括重复记录。

"prevunique"

此方向使游标在 源的末尾打开。迭代时, 游标不应 产生具有相同键的记录,但除此之外会 按键单调递减的顺序产生所有记录。对于每个具有 重复值的键,只会产生第一条记录。当 源是一个对象存储,或者是一个 索引且该索引的唯一 标志设置为 true 时,此方向 的行为与 "prev" 完全相同。

一个游标在其范围内具有一个位置。在游标完整的范围 尚未迭代完之前, 游标正在迭代的记录列表可能会 发生变化。 为了处理这种情况,游标不会将其位置保存为 索引,而是保存为先前返回 记录的键。对于向前迭代的游标, 下一次要求游标迭代到下一条记录时,它会返回 大于先前返回键的键中最低的记录。对于向后迭代的游标,情况相反, 它会返回小于先前返回键的键中最高的记录。

对于迭代索引的游标,情况稍微更加 复杂,因为多条记录可以具有相同的键,因此还会按 值排序。迭代索引时, 游标还具有一个对象存储 位置,它 表示索引中先前找到的记录的值。 在查找下一条合适的记录时,会同时使用位置和 对象存储 位置。

一个游标具有一个键和一个值, 它们 表示最后迭代的记录的键和值。

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

如果游标的源 是一个对象存储,则游标的 有效对象存储就是该对象存储, 而游标的有效键就是游标的 位置。如果游标的源是一个 索引, 则游标的有效对象存储是该索引所引用的对象 存储,而有效 键则是游标的 对象 存储位置。

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

一个游标还具有一个仅键标志,它 表示 游标的值是否 通过 API 暴露。

2.11. 键生成器

创建一个对象存储时, 可以指定其使用一个 键生成器。如果未另行指定, 键生成器用于为插入对象存储中的记录生成键。

一个键生成器具有一个当前数字。该 当前 数字始终是一个小于 或等于 253 (9007199254740992) + 1 的正整数。一个键生成器的当前 数字的初始值为 1,在创建关联的对象 存储时设置。随着键的生成, 当前 数字会递增, 并且可以通过使用显式键将其更新为特定值。

注意: 每个使用键生成器的对象存储都使用独立的 生成器。也就是说,与一个对象存储交互绝不会影响 任何其他对象存储的键生成器。

修改键生成器的当前数字被视为数据库操作的 一部分。这意味着,如果操作失败 并且操作被还原,当前数字也会 还原为操作开始之前的值。这 既适用于因使用键生成器时当前数字 增加 1 而发生的修改, 也适用于因在存储记录的调用中指定键值而存储 记录所导致的修改。

同样,如果一个事务被中止,则该事务作用域中 每个对象 存储的键生成器的当前数字 会还原为 事务 启动之前的值。

键生成器的当前数字绝不会减少,除非是数据库操作被还原的 结果。从一个对象存储中删除一条 记录绝不会影响该 对象存储的键生成器。即使清除对象存储中的所有记录, 例如使用 clear() 方法,也不会 影响对象存储键生成器的当前数字。

当存储一条记录,并且在存储记录的调用中 未指定键时, 会生成一个键。

要为一个对象存储 store 生成键, 运行以下步骤:

  1. 令 generator 为 store 的键生成器。

  2. 令 key 为 generator 的当前 数字。

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

  4. 将 generator 的当前数字增加 1。

  5. 返回 key。

当存储一条记录,并且在存储记录的调用中 指定了键时, 关联的键生成器可能 会被更新。

要使用 key 为一个对象存储 store 可能 更新键生成器, 运行以下步骤:

  1. 如果 key 的类型 不是数值,则中止这些步骤。

  2. 令 value 为 key 的值。

  3. 将 value 设置为 value 与 253 (9007199254740992) 中的较小者。

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

  5. 令 generator 为 store 的键生成器。

  6. 如果 value 大于或等于 generator 的当前 数字, 则将 generator 的当前数字设置为 value + 1。

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

只有指定的类型为 数值的键才能影响 键生成器的当前 数字。类型为 日期、数组(无论其中包含什么其他键)、二进制或字符串(无论 它们是否可以被解析为数字)的键都不会影响键生成器的当前数字。 类型为 数值且值小于 1 的键不会影响 当前 数字,因为它们始终小于 当前 数字。

当键生成器的当前数字超过 253 (9007199254740992) 时,之后任何尝试使用 键生成器生成新键的操作都会导致 一个 "ConstraintError" DOMException。 仍然可以通过指定显式 键向对象存储中插入 记录,但是若要再次对这些记录使用键生成器, 唯一的方法是删除对象存储并创建一个新的对象存储。

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

只要以正常方式使用键生成器,此限制就 不会成为问题。如果昼夜不停地每秒生成 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

从 objectStore 中移除项目绝不会影响键生成器。 包括调用 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
// 如果 objectStore 使用
// 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

以下示例说明了尝试 使用内联键和键生成器将对象保存 到对象存储时的不同行为。

如果以下条件为 true:

那么会使用键 生成器提供的值来填充 键值。在下面的示例中,对象存储的键路径为 "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);
};

如果以下条件为 true:

那么会使用与键路径 属性关联的值。不会使用自动生成的键。在下面的 示例中,对象 存储的键 路径为 "foo.bar"。实际对象的 bar 属性值为 10,即 { foo: { bar: 10} }。将对象保存到 对象存储时, bar 属性会保持其值 10,因为这就是 键值。

const store = db.createObjectStore("store", { keyPath: "foo.bar",
                                               autoIncrement: true });
store.put({ foo: { bar: 10 } }).onsuccess = function(e) {
  const key = e.target.result;
  console.assert(key === 10);
};

以下示例说明了指定的 内联键通过键路径 定义,但不存在与之匹配的属性时的情况。随后会使用 键生成器提供的值 来填充键值,并且 系统负责创建满足层级链上 属性依赖关系所需的任意数量的属性。 在下面的示例中, 对象存储的键路径为 "foo.bar.baz"。实际 对象的 foo 属性没有值,即 { zip: {} }。当对象被 保存到对象存储时, 会依次创建 foo、bar 和 baz 属性,使每个属性成为另一个属性的子级,直到可以为 foo.bar.baz 赋值。foo.bar.baz 的值 是对象存储生成的下一个键。

const store = db.createObjectStore("store", { keyPath: "foo.bar.baz",
                                               autoIncrement: true });
store.put({ zip: {} }).onsuccess = function(e) {
  const key = e.target.result;
  console.assert(key === 1);
  store.get(key).onsuccess = function(e) {
    const value = e.target.result;
    // value 将为:{ zip: {}, foo: { bar: { baz: 1 } } }
    console.assert(value.foo.bar.baz === 1);
  };
};

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

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

// 键生成将尝试在此原始值上创建并存储键路径
// 属性。
store.put(4); // 将抛出 DataError

2.12. 记录快照

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

一个记录快照具有一个 键,它是一个键。

一个记录快照具有一个 值,它是一个值。

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

一个记录快照还 具有一个主键,它是一个键。

注意: 对于索引 记录,快照的主键是该记录的值,即索引所引用的 对象存储中记录的键。 对于对象存储记录,快照的主键和键是同一个键,即该记录的键。

3. 异常

本文档中使用的每个异常都是一个 DOMException 或派生自 DOMException 的 接口,如 [WEBIDL] 中所定义。

下表列出了本文档中使用的 DOMException 名称,以及对异常 用法的说明。

类型 说明
AbortError 请求被中止。
ConstraintError 事务中的修改操作因 未满足约束而失败。
DataCloneError 要存储的数据无法被内部 结构化克隆算法克隆。
DataError 提供给操作的数据不符合要求。
InvalidAccessError 对对象执行了无效操作。
InvalidStateError 在不允许执行操作的对象上或不允许执行操作的时间 调用了某个操作,或者针对 已被删除或移除的源对象发出了请求。
NotFoundError 操作失败,因为找不到所请求的数据库 对象。
NotReadableError 操作失败,因为无法读取包含 所请求数据的底层存储。
SyntaxError keyPath 参数包含无效的键路径。
ReadOnlyError 尝试在只读事务中执行修改操作。
TransactionInactiveError 针对当前 非活动或已完成的事务发出了请求。
UnknownError 操作因与数据库本身无关的暂时性原因 或任何其他错误均未涵盖的原因而失败。
VersionError 尝试使用低于现有版本的版本 打开数据库。

除了上述 DOMException 名称外,如果操作因 剩余存储空间不足,或者已达到存储配额且 用户拒绝为数据库提供更多空间而失败,则应使用 QuotaExceededError 异常类型。

注意: 鉴于多个 Indexed DB 操作可能抛出相同类型的 错误,而且即使是单个操作也可能因多种原因抛出相同类型的 错误,因此鼓励实现 提供更具体的消息,以便开发者识别 错误原因。

4. API

API 方法会在不阻塞调用线程的情况下返回。所有 异步操作都会立即返回一个 IDBRequest 实例。该对象最初不包含有关 操作结果的任何信息。一旦信息可用, 就会在请求上触发一个事件,并且可以通过 IDBRequest 实例的属性访问该信息。

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

要排入数据库 任务,在数据库访问任务 源上执行排入 任务。

4.1. IDBRequest 接口

IDBRequest 接口提供一种使用 事件处理程序 IDL 属性 [HTML]访问针对数据库和 数据库对象的异步请求结果的方式。

每个用于发出异步请求的方法都会返回一个 IDBRequest 对象,该对象通过事件将信息传回发出请求的 应用程序。这种设计意味着任意数量的 请求都可以在任意数据库上同时处于活动状态。

在以下示例中,我们异步打开一个数据库。 注册了各种事件处理程序以响应各种 情况。

const request = indexedDB.open('AddressBook', 15);
request.onsuccess = function(evt) {...};
request.onerror = function(evt) {...};
[Exposed=(Window,Worker)]
interface IDBRequest : EventTarget {
  readonly attribute any result;
  readonly attribute DOMException? error;
  readonly attribute (IDBObjectStore or IDBIndex or IDBCursor)? source;
  readonly attribute IDBTransaction? transaction;
  readonly attribute IDBRequestReadyState readyState;

  // 事件处理程序:
  attribute EventHandler onsuccess;
  attribute EventHandler onerror;
};

enum IDBRequestReadyState {
  "pending",
  "done"
};
request . result

请求完成后,返回结果; 如果请求失败,则返回 undefined。如果请求仍处于待处理状态,则抛出一个 "InvalidStateError" DOMException。

request . error

请求完成后,返回错误(一个 DOMException); 如果请求成功,则返回 null。如果请求仍处于待处理状态,则抛出 一个 "InvalidStateError" DOMException。

request . source

返回发出请求所针对的 IDBObjectStore、 IDBIndex 或 IDBCursor; 如果它是一个打开请求,则返回 null。

request . transaction

返回发出该请求所在的 IDBTransaction。 如果这是一个打开 请求,则在其存活期间返回一个 升级 事务,否则返回 null。

request . readyState

在请求完成之前返回 "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 的事务。

注意:对于某些请求,例如由 open() 返回的请求,transaction getter 可以返回 null。

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

onsuccess 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型为 success。

onerror 属性是一个事件处理程序 IDL 属性,其事件处理程序事件类型为 error 事件。

IDBDatabase 上返回打开 请求的方法使用一个 扩展接口,以允许监听 blocked 和 upgradeneeded 事件。

[Exposed=(Window,Worker)]
interface IDBOpenDBRequest : IDBRequest {
  // 事件处理程序:
  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 构造事件中的定义进行构造。

给定 oldVersion 和 newVersion,要在 target 上触发 名为 e 的版本更改事件,运行以下步骤:

  1. 令 event 为使用 IDBVersionChangeEvent 创建 事件的结果。

  2. 将 event 的 type 属性设置为 e。

  3. 将 event 的 bubbles 和 cancelable 属性设置为 false。

  4. 将 event 的 oldVersion 属性设置为 oldVersion。

  5. 将 event 的 newVersion 属性设置为 newVersion。

  6. 令 legacyOutputDidListenersThrowFlag 为 false。

  7. 使用 legacyOutputDidListenersThrowFlag 在 target 上分派 event。

  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。 如果请求成功,request 的 result 将是该连接。

request = indexedDB . open(name, version)

尝试使用指定的 version 打开与 指定数据库的连接。如果数据库已经以 较低版本存在,并且存在未响应 versionchange 事件而关闭的打开连接, 则请求会被 阻塞,直到它们全部关闭,随后执行升级。 如果数据库已经以更高 版本存在,则请求会失败。如果请求 成功,request 的 result 将 是该连接。

request = indexedDB . deleteDatabase(name)

尝试删除指定的数据库。 如果 数据库已经存在,并且存在未响应 versionchange 事件而关闭的打开 连接,则请求会被 阻塞,直到它们全部关闭。如果请求 成功,request 的 result 将为 null。

result = await indexedDB . databases()

返回一个 promise,其会兑现为一个对象列表,该列表提供 存储 键内数据库名称和版本的快照。

此 API 旨在让 Web 应用程序审查数据库的使用情况, 例如清理网站代码早期版本留下的数据。请注意, 结果是一个快照;对于数据 收集的顺序或响应的交付,相对于此上下文或其他上下文 创建、升级或删除数据库的请求,不提供任何保证。

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

  1. 如果 version 为 0(零),则抛出一个 TypeError。

  2. 令 environment 为this 的相关设置对象。

  3. 令 storageKey 为给定 environment 运行获取 存储键的结果。 如果返回失败,则抛出一个 "SecurityError" DOMException 并中止这些步骤。

  4. 令 request 为一个新的打开请求。

  5. 并行运行以下步骤:

    1. 令 result 为使用 storageKey、 name、给定时的 version(否则为 undefined) 以及 request 打开数据库连接的结果。

      如果未给出 version 会怎样? 如果未给出 version,并且已经存在具有该名称的数据库, 则会在不更改版本的情况下打开连接。如果 未给出 version,并且不存在具有该名称的数据库,则会创建一个新的数据库, 其 版本等于 1。
    2. 将 request 的已处理标志设置为 true。

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

      1. 如果 result 是一个错误,则:

        1. 将 request 的结果设置为 undefined。

        2. 将 request 的错误设置为 result。

        3. 将 request 的完成 标志设置为 true。

        4. 在 request 上触发一个事件,名称为 error, 并将其 bubbles 和 cancelable 属性初始化为 true。

      2. 否则:

        1. 将 request 的结果设置为 result。

        2. 将 request 的完成 标志设置为 true。

        3. 在 request 上触发一个事件,名称为 success。

        注意: 如果上述步骤导致运行了一个升级 事务,这些步骤将在 该事务完成后运行。这可以确保在 即将发生另一次版本升级的情况下, 先在连接上触发 success 事件,从而 使脚本有机会为 versionchange 事件注册监听器。

        为什么不使用触发成功事件或 触发错误 事件的步骤? 此时没有与请求关联的事务,因此那些步骤—— 它们会在分派前激活关联事务,并在分派后停用 事务——并不适用。
  6. 为 request 返回一个新的 IDBOpenDBRequest 对象。

deleteDatabase(name) 方法的步骤 如下:

  1. 令 environment 为this 的相关设置对象。

  2. 令 storageKey 为给定 environment 运行获取 存储键的结果。 如果返回失败,则抛出一个 "SecurityError" DOMException 并中止这些步骤。

  3. 令 request 为一个新的打开请求。

  4. 并行运行以下步骤:

    1. 令 result 为使用 storageKey、 name 和 request 删除 数据库的结果。

    2. 将 request 的已处理标志设置为 true。

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

      1. 如果 result 是一个错误, 则将 request 的错误设置为 result, 将 request 的完成标志设置为 true, 并在 request 上触发一个事件, 名称为 error, 并将其 bubbles 和 cancelable 属性初始化为 true。

      2. 否则, 将 request 的结果设置为 undefined, 将 request 的完成标志设置为 true, 并在request 上使用 result 和 null 触发版本更改事件,名称为 success。

        为什么不使用触发成功事件或 触发 错误事件的步骤? 没有与请求关联的事务,因此 那些步骤——它们会在分派前激活关联 事务,并在分派后停用 事务——并不适用。

        此外,此处的 success 事件是一个 IDBVersionChangeEvent, 其中包含 oldVersion 和 newVersion 详细信息。

  5. 为 request 返回一个新的 IDBOpenDBRequest 对象。

databases() 方法的步骤如下:

  1. 令 environment 为this 的相关设置对象。

  2. 令 storageKey 为给定 environment 运行获取 存储键的结果。 如果返回失败,则返回一个以 "SecurityError" DOMException 拒绝的 promise。

  3. 令 p 为一个新的 promise。

  4. 并行运行以下步骤:

    1. 令 databases 为 storageKey 中 数据库的集合。 如果由于任何原因无法确定这一点,则排入一个数据库 任务,以使用适当的错误(例如一个 "UnknownError" DOMException)拒绝 p, 并终止这些步骤。

    2. 令 result 为一个新的列表。

    3. 对于 databases 中的每个 db:

      1. 如果 db 的版本为 0,则继续。

      2. 令 info 为一个新的 IDBDatabaseInfo 字典。

      3. 将 info 的 name 字典成员设置为 db 的名称。

      4. 将 info 的 version 字典成员设置为 db 的版本。

      5. 将 info 追加到 result。

    4. 排入一个 数据库任务,以使用 result 兑现 p。

  5. 返回 p。

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

将两个值作为键进行比较。如果 key1 在 key2 之前,则返回 -1;如果 key2 在 key1 之前,则返回 1;如果 两个键相等,则返回 0。

如果任一输入不是有效的键, 则抛出一个 "DataError" DOMException。

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

  1. 令 a 为使用 first 将值转换为键的结果。重新抛出任何异常。

  2. 如果 a 为 "invalid value" 或 "invalid type",则抛出一个 "DataError" DOMException。

  3. 令 b 为使用 second 将值转换为键的结果。重新抛出任何异常。

  4. 如果 b 为 "invalid value" 或 "invalid type",则抛出一个 "DataError" DOMException。

  5. 返回使用 a 和 b 比较两个键的结果。

4.4. IDBDatabase 接口

IDBDatabase 接口表示与数据库的连接。

如果一个 IDBDatabase 对象所关联连接的关闭 待处理标志为 false,并且它 注册了一个或多个类型为以下之一的事件监听器: abort、 error 或 versionchange, 则该对象不得被垃圾回收。 如果一个 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);

  // 事件处理程序:
  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 所关联数据库的名称。

注意: 即使 this 的关闭 待处理标志为 true,name 属性也会返回此名称。换句话说,此属性的值在 IDBDatabase 实例的整个生命周期内保持不变。

version getter 的步骤是 返回this 的版本。

这与数据库的 版本相同吗? 只要连接保持打开, 它就与所连接数据库的版本相同。但一旦 连接已经关闭,此 属性就不会 反映之后通过升级事务所做的更改。
connection . objectStoreNames

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

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

使用给定的 name 和 options 创建一个新的对象 存储,并返回一个新的 IDBObjectStore。

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

connection . deleteObjectStore(name)

删除具有给定 name 的对象 存储。

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

objectStoreNames getter 的步骤如下:
  1. 令 names 为this 的对象存储集合中对象存储的名称列表。

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

这与数据库的 对象存储 名称相同吗? 只要连接保持打开, 它就与所连接数据库的对象存储名称相同。 但一旦连接已经关闭,此 属性 就不会反映之后通过升级事务所做的更改。

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

  1. 令 database 为this 所关联的数据库。

  2. 如果 database 的升级 事务 不为 null,则令 transaction 为该事务;否则抛出一个 "InvalidStateError" DOMException。

  3. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  4. 如果 options 的 keyPath 成员既不是 undefined 也不是 null,则令 keyPath 为该成员,否则为 null。

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

  6. 如果 database 中已经存在一个对象存储, 其名称为 name, 则抛出一个 "ConstraintError" DOMException。

  7. 令 autoIncrement 为 options 的 autoIncrement 成员。

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

  9. 令 store 为 database 中一个新的对象存储。将所创建对象存储的 名称设置为 name。如果 autoIncrement 为 true,则所创建的对象存储使用一个键生成器。如果 keyPath 不为 null,则将所创建对象 存储的 键 路径设置为 keyPath。

  10. 返回一个与 store 和 transaction 关联的新对象 存储句柄。

此方法在已连接的数据库中创建并返回一个具有给定 名称的新对象存储。请注意,此方法必须 仅从升级事务内部调用。

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

在某些实现中,可能会在 createObjectStore() 方法返回后,为创建对象存储而排入任务之后遇到问题。例如,在 某些实现中,新创建对象存储的元数据会异步插入数据库,或者 实现可能因为配额 原因需要请求用户许可。此类实现仍然必须创建并返回一个 IDBObjectStore 对象,并且一旦实现确定 创建对象存储失败,就必须使用适当的错误, 按照中止事务的步骤中止 事务。例如,如果创建对象存储 因配额原因失败,则必须使用 QuotaExceededError 作为 错误。

deleteObjectStore(name) 方法的 步骤如下:

  1. 令 database 为this 所关联的数据库。

  2. 如果 database 的升级 事务 不为 null,则令 transaction 为该事务;否则抛出一个 "InvalidStateError" DOMException。

  3. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  4. 令 store 为 database 中 名称为 name 的对象 存储; 如果不存在,则抛出一个 "NotFoundError" DOMException。

  5. 从this 的对象存储集合中移除 store。

  6. 如果存在与 store 和 transaction 关联的对象 存储句柄,则从其 索引集合中移除所有条目。

  7. 销毁 store。

此方法销毁已连接的数据库中具有给定名称的对象 存储。请注意,此方法必须仅从 升级 事务内部调用。

此方法会同步修改调用它的 IDBDatabase 实例上的 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. 如果 storeNames 是序列,则令 scope 为其中唯一字符串的集合, 否则令其为包含一个等于 storeNames 的字符串的集合。

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

  5. 如果 scope 为空,则抛出一个 "InvalidAccessError" DOMException。

  6. 如果 mode 既不是 "readonly" 也不是 "readwrite", 则抛出 一个 TypeError。

  7. 令 transaction 为一个新创建的事务,其使用此连接、 mode、options 的 durability 成员,以及 scope 中命名的对象 存储集合。

  8. 将 transaction 的清理事件循环设置为当前 事件循环。

  9. 返回一个表示 transaction 的 IDBTransaction 对象。

🚧 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. 令 transaction 为this 的事务。

  3. 令 store 为this 的对象存储。

  4. 如果 store 已被删除, 则抛出 一个 "InvalidStateError" DOMException。

  5. 如果 transaction 不是一个升级事务, 则抛出 一个 "InvalidStateError" DOMException。

  6. 如果 transaction 的状态不是活动, 则抛出 一个 "TransactionInactiveError" DOMException。

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

  8. 如果 store 的数据库中已经存在一个对象存储, 其名称为 name,则抛出一个 "ConstraintError" DOMException。

  9. 将 store 的名称设置为 name。

  10. 将this 的名称设置为 name。

keyPath getter 的步骤是 返回this 的对象存储的键路径; 如果没有,则返回 null。按照 [WEBIDL], 键路径会被转换为 DOMString (如果是 字符串)或 sequence<DOMString> (如果是字符串列表)。

注意: 返回的值与创建 对象存储时使用的实例不同。 但是,如果此属性返回 一个对象(具体来说是一个 Array), 那么每次检查时都会返回同一个对象 实例。更改该 对象的属性不会影响对象 存储。

indexNames getter 的步骤如下:
  1. 令 names 为this 的索引集合中索引的名称列表。

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

这与对象 存储的索引名称列表相同吗? 只要事务尚未完成, 它就与关联对象 存储的索引名称列表相同。但一旦 事务已经 完成,此 属性将不会 反映之后通过升级事务所做的更改。

transaction getter 的步骤是 返回this 的事务。

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

如果在 只读事务中调用,以下方法会抛出一个 "ReadOnlyError" DOMException; 如果 在事务并非活动状态时 调用,则会抛出一个 "TransactionInactiveError" DOMException。
request = store . put(value [, key])
request = store . add(value [, key])

使用给定的 value 和 key 在 store 中添加或更新一条记录。

如果存储使用内联键并且指定了 key,则会抛出一个 "DataError" DOMException。

如果使用 put(), 则任何具有该键的现有记录 都会被替换。如果 使用 add(), 并且已经存在一条具有该键的记录, 则 request 将失败,并将 request 的 error 设置为一个 "ConstraintError" DOMException。

如果成功,request 的 result 将是该 记录的键。

request = store . delete(query)

删除 store 中 具有给定键或位于 query 中给定键范围内的记录。

如果成功,request 的 result 将 为 undefined。

request = store . clear()

删除 store 中的所有记录。

如果成功,request 的 result 将 为 undefined。

put(value, key) 方法的步骤是: 使用this、 value、key 和值为 false 的 no-overwrite flag 运行添加或 放置,并返回其结果。

add(value, key) 方法的步骤是: 使用this、 value、key 和值为 true 的 no-overwrite flag 运行添加或 放置,并返回其结果。

要使用 handle、value、key 和 no-overwrite flag 添加或放置,运行以下步骤:

  1. 令 transaction 为 handle 的 事务。

  2. 令 store 为 handle 的 对象存储。

  3. 如果 store 已被删除, 则抛出 一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "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. 令 key 为 r。

  9. 令 targetRealm 为用户代理定义的Realm。

  10. 令 clone 为在 transaction 期间 targetRealm 中 value 的一个克隆。 重新抛出任何异常。

    为什么要创建值的副本? 值在存储时会被序列化。在这里将其视为副本 可使本规范中的其他算法将其视为 ECMAScript 值,但如果行为差异不可观察,实现可以对此进行 优化。
  11. 如果 store 使用内联键,则:

    1. 令 kpk 为使用 clone 和 store 的键路径使用键路径从 值中提取键的结果。重新抛出任何 异常。

    2. 如果 kpk 无效,则抛出 一个 "DataError" DOMException。

    3. 如果 kpk 不是失败,则令 key 为 kpk。

    4. 否则(kpk 为失败):

      1. 如果 store 没有键生成器,则抛出 一个 "DataError" DOMException。

      2. 如果使用 clone 和 store 的键 路径运行检查键是否可以 注入值返回 false,则抛出一个 "DataError" DOMException。

  12. 令 operation 为一个算法,用于使用 store、clone、key 和 no-overwrite flag 运行将记录存储到对象存储。

  13. 返回使用 handle 和 operation 运行异步执行请求的结果(一个 IDBRequest)。

delete(query) 方法的步骤如下:

  1. 令 transaction 为this 的事务。

  2. 令 store 为this 的对象存储。

  3. 如果 store 已被删除,则抛出一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 如果 transaction 是一个只读事务, 则抛出 一个 "ReadOnlyError" DOMException。

  6. 令 range 为使用 query 和 true 将值转换为键范围的结果。重新抛出任何异常。

  7. 令 operation 为一个算法,用于使用 store 和 range 运行从对象存储删除 记录。

  8. 返回使用this 和 operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是一个键 或用于标识要删除记录的键范围(一个 IDBKeyRange)。

注意: 与其他接受键或键范围的方法不同,此方法 不允许将 null 作为键传入。这是为了 降低一个小错误清空整个对象存储的风险。

clear() 方法的步骤如下:

  1. 令 transaction 为this 的事务。

  2. 令 store 为this 的对象存储。

  3. 如果 store 已被删除,则抛出一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 如果 transaction 是一个只读事务, 则抛出 一个 "ReadOnlyError" DOMException。

  6. 令 operation 为一个算法,用于使用 store 运行清空对象存储。

  7. 返回使用this 和 operation 运行异步执行请求的结果(一个 IDBRequest)。

如果在事务并非 活动状态时 调用,以下方法会抛出一个 "TransactionInactiveError" DOMException。
request = store . get(query)

检索与 query 中给定键或键范围匹配的第一条 记录的值。

如果成功,request 的 result 将是该 值;如果没有匹配的 记录,则为 undefined。

request = store . getKey(query)

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

如果成功,request 的 result 将是该 键;如果没有匹配的 记录,则为 undefined。

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

检索与 query 中给定键或键范围匹配的记录的值(如果给出了 count, 则最多检索 count 个)。 将 direction 选项设置为 "next" 以检索前 count 个值,或设置为 "prev" 以返回后 count 个值。

如果成功,request 的 result 将是一个包含这些值的 Array。

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

检索与 query 中给定键或键范围匹配的记录的键(如果给出了 count, 则最多检索 count 个)。 将 direction 选项设置为 "next" 以检索前 count 个键,或设置为 "prev" 以返回后 count 个键。

如果成功,request 的 result 将是一个包含这些键的 Array。

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

检索记录的键和值。

query 选项指定要匹配的键或 键范围。 count 选项限制匹配的记录数量。 将 direction 选项设置为 "next" 以检索前 count 条记录,或设置为 "prev" 以返回后 count 条记录。

如果成功,request 的 result 将是一个 Array, 其中每个成员都是一个 IDBRecord。

request = store . count(query)

检索与 query 中给定键或键范围匹配的记录数量。

如果成功,request 的 result 将是该计数。

get(query) 方法的步骤如下:

  1. 令 transaction 为this 的事务。

  2. 令 store 为this 的对象存储。

  3. 如果 store 已被删除,则抛出一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 令 range 为使用 query 和 true 将值转换为键范围的结果。重新抛出任何异常。

  6. 令 operation 为一个算法,用于使用当前 Realm 记录、store 和 range 运行从对象存储检索 值。

  7. 返回使用this 和 operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是一个键 或键范围(一个 IDBKeyRange), 用于标识要检索的记录值。如果指定了 范围,该方法会检索该范围内第一个存在的值。

注意: 如果不存在具有给定键的记录,此方法产生的结果 与存在记录但其值为 undefined 时相同。 如果需要区分这两种情况,可以使用 具有相同键的 openCursor()。 如果记录存在,这将返回 一个值为 undefined 的游标;如果不存在这样的记录,则不会返回游标。

getKey(query) 方法的步骤如下:

  1. 令 transaction 为this 的事务。

  2. 令 store 为this 的对象存储。

  3. 如果 store 已被删除,则抛出一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 令 range 为使用 query 和 true 将值转换为键范围的结果。重新抛出任何异常。

  6. 令 operation 为一个算法,用于使用 store 和 range 运行从对象存储检索 键。

  7. 返回使用this 和 operation 运行异步执行请求的结果(一个 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. 令 transaction 为this 的事务。

  2. 令 store 为this 的对象存储。

  3. 如果 store 已被删除,则抛出一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 令 range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. 令 operation 为一个算法,用于使用 store 和 range 运行对范围中的 记录计数。

  7. 返回使用this 和 operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是一个键 或键范围(一个 IDBKeyRange), 用于标识要计数的记录。如果为 null 或未 给出,则使用一个无界键 范围。

如果在事务并非 活动状态时 调用,以下方法会抛出一个 "TransactionInactiveError" DOMException。
request = store . openCursor([query [, direction = "next"]])

在与 query 匹配的记录上打开一个游标, 并按照 direction 排序。如果 query 为 null,则匹配 store 中的所有记录。

如果成功,request 的 result 将是一个 指向第一条匹配 记录的 IDBCursorWithValue;如果没有匹配的记录, 则为 null。

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

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

如果成功,request 的 result 将是一个 指向第一条匹配记录的 IDBCursor;如果没有匹配的记录, 则为 null。

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

  1. 令 transaction 为this 的事务。

  2. 令 store 为this 的对象存储。

  3. 如果 store 已被删除,则抛出一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 令 range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. 令 cursor 为一个新的游标,其 源 句柄设置为this, 位置为 undefined, 方向 设置为 direction, 已获取 值标志设置为 false, 键和值为 undefined, 范围设置为 range,并且 仅键 标志设置为 false。

  7. 令 operation 为一个算法,用于使用当前 Realm 记录和 cursor 运行迭代游标。

  8. 令 request 为使用this 和 operation 运行异步 执行请求的结果。

  9. 将 cursor 的请求设置为 request。

  10. 返回 request。

注意: query 参数可以是一个键 或键范围(一个 IDBKeyRange), 用作游标的范围。 如果为 null 或未给出,则使用一个无界键范围。

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

  1. 令 transaction 为this 的事务。

  2. 令 store 为this 的对象存储。

  3. 如果 store 已被删除,则抛出一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 令 range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. 令 cursor 为一个新的游标,其 源 句柄设置为this, 位置为 undefined, 方向 设置为 direction, 已获取 值标志设置为 false, 键和值为 undefined, 范围设置为 range,并且 仅键 标志设置为 true。

  7. 令 operation 为一个算法,用于使用当前 Realm 记录和 cursor 运行迭代游标。

  8. 令 request 为使用this 和 operation 运行异步 执行请求的结果。

  9. 将 cursor 的请求设置为 request。

  10. 返回 request。

注意: query 参数可以是一个键 或键范围(一个 IDBKeyRange), 用作游标的范围。如果为 null 或未给出,则使用一个无界 键范围。

index = store . index(name)

返回 store 中名称为 name 的索引对应的 IDBIndex。

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

使用给定的 name、 keyPath 和 options 在 store 中创建一个新的索引,并返回一个新的 IDBIndex。 如果 keyPath 和 options 定义了无法由 store 中现有数据满足的约束,则升级事务将以 一个 "ConstraintError" DOMException 中止。

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

store . deleteIndex(name)

删除 store 中具有给定 name 的索引。

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

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

  1. 令 transaction 为this 的事务。

  2. 令 store 为this 的对象存储。

  3. 如果 transaction 不是一个升级事务, 则抛出 一个 "InvalidStateError" DOMException。

  4. 如果 store 已被删除,则抛出一个 "InvalidStateError" DOMException。

  5. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  6. 如果 store 中已经存在一个索引, 其名称为 name,则抛出一个 "ConstraintError" DOMException。

  7. 如果 keyPath 不是一个有效键路径,则抛出 一个 "SyntaxError" DOMException。

  8. 令 unique 为 options 的 unique 成员。

  9. 令 multiEntry 为 options 的 multiEntry 成员。

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

  11. 令 index 为 store 中一个新的索引。 将 index 的名称设置为 name,将键 路径设置为 keyPath, 将唯一 标志设置为 unique,并将 multiEntry 标志设置为 multiEntry。

  12. 将 index 添加到this 的索引集合。

  13. 返回一个与 index 和this 关联的新索引 句柄。

此方法在对象存储中创建并返回一个具有给定名称的新索引。 请注意,此方法 必须仅从升级事务内部调用。

请求创建的索引可以对 索引所引用的对象存储中允许的数据施加约束,例如 要求索引键路径所引用的值 唯一。如果所引用的对象存储 已经包含违反这些约束的数据, 这不得导致 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. 令 transaction 为this 的事务。

  2. 令 store 为this 的对象存储。

  3. 如果 store 已被删除,则抛出一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态为已完成, 则抛出一个 "InvalidStateError" DOMException。

  5. 如果存在,则令 index 为this 的 索引集合中 名称为 name 的索引;否则抛出 一个 "NotFoundError" DOMException。

  6. 返回一个与 index 和this 关联的索引 句柄。

注意: 在同一个 IDBObjectStore 实例上使用相同名称 每次调用此方法都会返回同一个 IDBIndex 实例。

注意: 返回的 IDBIndex 实例 特定于此 IDBObjectStore 实例。如果在另一个 IDBObjectStore 实例上以相同名称调用此方法, 则会返回另一个 IDBIndex 实例。

deleteIndex(name) 方法的步骤 如下:

  1. 令 transaction 为this 的事务。

  2. 令 store 为this 的对象存储。

  3. 如果 transaction 不是一个升级事务, 则抛出 一个 "InvalidStateError" DOMException。

  4. 如果 store 已被删除,则抛出一个 "InvalidStateError" DOMException。

  5. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  6. 如果存在,则令 index 为 store 中名称为 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. 令 transaction 为this 的事务。

  3. 令 index 为this 的索引。

  4. 如果 transaction 不是一个升级事务, 则抛出 一个 "InvalidStateError" DOMException。

  5. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  6. 如果 index 或 index 的对象存储已 被删除,则抛出一个 "InvalidStateError" DOMException。

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

  8. 如果 index 的对象存储中已经存在一个索引, 其名称为 name,则抛出一个 "ConstraintError" DOMException。

  9. 将 index 的名称设置为 name。

  10. 将this 的名称设置为 name。

objectStore getter 的步骤是 返回this 的对象存储句柄。

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

注意: 返回的值与创建 索引时使用的实例不同。但是, 如果此属性返回一个 对象(具体来说是一个 Array), 那么每次检查时都会返回同一个对象 实例。更改该 对象的属性不会影响索引。

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

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

如果在事务并非 活动状态时 调用,以下方法会抛出一个 "TransactionInactiveError" DOMException。
request = index . get(query)

检索与 query 中给定键或键范围匹配的第一条 记录的值。

如果成功,request 的 result 将是该 值;如果没有匹配的 记录,则为 undefined。

request = index . getKey(query)

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

如果成功,request 的 result 将是该 键;如果没有匹配的 记录,则为 undefined。

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

检索与 query 中给定键或键范围匹配的记录的值(如果给出了 count, 则最多检索 count 个)。 将 direction 选项设置为 "next" 以检索前 count 个值,设置为 "prev" 以返回后 count 个值。 将 direction 选项设置为 "nextunique" 或 "prevunique" 可在检索具有重复索引键的第一条记录后排除具有重复索引键的记录。

如果成功,request 的 result 将是一个包含这些值的 Array。

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

检索与 query 中给定键或键范围匹配的记录的键(如果给出了 count, 则最多检索 count 个)。 将 direction 选项设置为 "next" 以检索前 count 个键,设置为 "prev" 以返回后 count 个键。 将 direction 选项设置为 "nextunique" 或 "prevunique" 可在检索具有重复索引键的第一条记录后排除具有重复索引键的记录。

如果成功,request 的 result 将是一个包含这些键的 Array。

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

检索记录的键、值和索引键。

query 选项指定要匹配的键或 键范围。 count 选项限制匹配的记录数量。 将 direction 选项设置为 "next" 以检索前 count 条记录,设置为 "prev" 以返回后 count 条记录。 将 direction 选项设置为 "nextunique" 或 "prevunique" 可在检索具有重复索引键的第一条记录后排除具有重复索引键的记录。

如果成功,request 的 result 将是一个 Array, 其中每个成员都是一个 IDBRecord。 使用 IDBRecord’s key 获取记录的索引键。使用 IDBRecord’s primaryKey 获取记录的键。

request = index . count(query)

检索与 query 中给定键 或键范围匹配的记录数量。

如果成功,request 的 result 将是该 计数。

get(query) 方法的步骤如下:

  1. 令 transaction 为this 的事务。

  2. 令 index 为this 的索引。

  3. 如果 index 或 index 的对象存储已 被删除,则抛出一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 令 range 为使用 query 和 true 将值转换为键范围的结果。重新抛出任何异常。

  6. 令 operation 为一个算法,用于使用当前 Realm 记录、index 和 range 运行从索引检索引用值。

  7. 返回使用this 和 operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是一个键 或键范围(一个 IDBKeyRange), 用于标识要检索的引用值。如果指定了 范围,该方法会检索该范围内第一条现有记录。

注意: 如果不存在具有给定键的记录,此方法产生的结果 与存在记录但其值为 undefined 时相同。 如果需要区分这两种情况,可以使用 具有相同键的 openCursor()。 如果记录存在,这将返回一个 值为 undefined 的游标;如果不存在这样的记录,则不会返回游标。

getKey(query) 方法的步骤如下:

  1. 令 transaction 为this 的事务。

  2. 令 index 为this 的索引。

  3. 如果 index 或 index 的对象存储已被删除, 则抛出 一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 令 range 为使用 query 和 true 将值转换为键范围的结果。重新抛出任何异常。

  6. 令 operation 为一个算法,用于使用 index 和 range 运行从索引检索 值。

  7. 返回使用this 和 operation 运行异步执行请求的结果(一个 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. 令 transaction 为this 的事务。

  2. 令 index 为this 的索引。

  3. 如果 index 或 index 的对象存储已 被删除,则抛出一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 令 range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. 令 operation 为一个算法,用于使用 index 和 range 运行对范围中的 记录计数。

  7. 返回使用this 和 operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: query 参数可以是一个键 或键范围(一个 IDBKeyRange), 用于标识要计数的记录。如果为 null 或未 给出,则使用一个无界键 范围。

如果在事务并非 活动状态时 调用,以下方法会抛出一个 "TransactionInactiveError" DOMException。
request = index . openCursor([query [, direction = "next"]])

在与 query 匹配的记录上打开一个游标, 并按照 direction 排序。如果 query 为 null,则匹配 index 中的所有记录。

如果成功,request 的 result 将是一个 IDBCursorWithValue; 如果没有匹配的 记录,则为 null。

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

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

如果成功,request 的 result 将是一个 IDBCursor; 如果没有匹配的记录,则为 null。

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

  1. 令 transaction 为this 的事务。

  2. 令 index 为this 的索引。

  3. 如果 index 或 index 的对象存储已被删除, 则抛出 一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 令 range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. 令 cursor 为一个新的游标,其 源 句柄设置为this, 位置为 undefined, 方向 设置为 direction, 已获取 值标志设置为 false, 键和值为 undefined, 范围设置为 range,并且 仅键 标志设置为 false。

  7. 令 operation 为一个算法,用于使用当前 Realm 记录和 cursor 运行迭代游标。

  8. 令 request 为使用this 和 operation 运行异步 执行请求的结果。

  9. 将 cursor 的请求设置为 request。

  10. 返回 request。

注意: query 参数可以是一个键 或键范围(一个 IDBKeyRange), 用作游标的范围。如果为 null 或未给出,则使用一个无界 键范围。

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

  1. 令 transaction 为this 的事务。

  2. 令 index 为this 的索引。

  3. 如果 index 或 index 的对象存储已 被删除,则抛出一个 "InvalidStateError" DOMException。

  4. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  5. 令 range 为使用 query 将值转换为键范围的结果。 重新抛出任何异常。

  6. 令 cursor 为一个新的游标,其 源 句柄设置为this, 位置为 undefined, 方向 设置为 direction, 已获取 值标志设置为 false, 键和值为 undefined, 范围设置为 range,并且 仅键 标志设置为 true。

  7. 令 operation 为一个算法,用于使用当前 Realm 记录和 cursor 运行迭代游标。

  8. 令 request 为使用this 和 operation 运行异步 执行请求的结果。

  9. 将 cursor 的请求设置为 request。

  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;

  // 静态构造方法:
  [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 的步骤是: 如果this 的下界 不为 null,则使用该值运行将键转换为值并返回结果; 否则返回 undefined。

upper getter 的步骤是: 如果this 的上界不为 null,则使用该值运行将键转换为值并返回结果; 否则 返回 undefined。

lowerOpen getter 的步骤是 返回this 的下界 开放标志。

upperOpen getter 的步骤是 返回this 的上界 开放标志。

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]])

返回一个从 lower 跨越到 upper 的新 IDBKeyRange。 如果 lowerOpen 为 true,则 lower 不包含在范围内。 如果 upperOpen 为 true,则 upper 不包含在范围内。

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

返回打开游标所用的 IDBObjectStore 或 IDBIndex。

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 返回一个对象(例如 Date 或 Array), 那么在游标的键发生 更改之前,每次检查时都会返回同一个对象实例。这 意味着,如果该对象被修改,任何检查游标值的人都会看到 这些修改。但是,修改这样的 对象不会修改数据库的内容。

primaryKey getter 的步骤是 使用游标当前的有效键运行将键转换为值并返回其结果。

注意: 如果 primaryKey 返回一个对象(例如 Date 或 Array), 那么在游标的有效 键发生更改之前,每次检查时都会返回同一个对象实例。这意味着,如果 该对象被修改,任何检查游标值的人都会看到 这些修改。但是,修改这样的对象 不会修改数据库的内容。

request getter 的步骤是 返回this 的 请求。

🚧 request 属性是本版新增的。 Chrome 76、Edge 79、Firefox 77 和 Safari 15 支持该属性。 🚧
以下方法会推进一个游标。 游标 前进后,会在打开游标时返回的同一个 IDBRequest 上触发一个 success 事件。如果范围内存在一条记录, result 将是同一个游标,否则为 undefined。

如果在游标已经前进时调用,则会抛出一个 "InvalidStateError" DOMException。

如果在事务 并非活动状态时 调用,以下方法会抛出一个 "TransactionInactiveError" DOMException。

cursor . advance(count)

使游标在范围内前进经过接下来的 count 条记录。

cursor . continue()

使游标前进到范围内的下一条记录。

cursor . continue(key)

使游标前进到范围内与 key 匹配或位于其 之后的下一条记录。

cursor . continuePrimaryKey(key, primaryKey)

使游标前进到范围内与 key 和 primaryKey 匹配或位于其后的下一条记录。如果源不是 一个索引,则抛出一个 "InvalidAccessError" DOMException。

advance(count) 方法的步骤如下:

  1. 如果 count 为 0(零),则抛出一个 TypeError。

  2. 令 transaction 为this 的事务。

  3. 如果 transaction 的状态不是活动, 则抛出一个 "TransactionInactiveError" DOMException。

  4. 如果this 的源或有效 对象存储已被删除,则抛出一个 "InvalidStateError" DOMException。

  5. 如果this 的已获取值标志 为 false,表示 游标正在被迭代或已经迭代超过其末尾, 则抛出 一个 "InvalidStateError" DOMException。

  6. 将this 的已获取值标志 设置为 false。

  7. 令 request 为this 的请求。

  8. 将 request 的已处理标志设置为 false。

  9. 将 request 的完成标志设置为 false。

  10. 令 operation 为一个算法,用于使用当前 Realm 记录、this 和 count 运行迭代游标。

  11. 使用this 的源句柄、 operation 和 request 运行异步执行请求。

注意: 在加载新的游标数据之前多次调用此方法——例如,从同一个 onsuccess 处理程序中调用 advance() 两次——会导致第二次调用抛出一个 "InvalidStateError" DOMException, 因为游标的 已获取值标志 已被设置为 false。

continue(key) 方法的步骤如下:

  1. 令 transaction 为this 的事务。

  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. 令 key 为 r。

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

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

  6. 将this 的已获取值标志 设置为 false。

  7. 令 request 为this 的请求。

  8. 将 request 的已处理标志设置为 false。

  9. 将 request 的完成标志设置为 false。

  10. 令 operation 为一个算法,用于使用当前 Realm 记录、this 和 给定时的 key 运行迭代游标。

  11. 使用this 的源句柄、 operation 和 request 运行异步执行请求。

注意: 在加载新的游标数据之前多次调用此方法——例如,从同一个 onsuccess 处理程序中调用 continue() 两次——会导致第二次调用抛出一个 "InvalidStateError" DOMException, 因为游标的 已获取值标志 已被设置为 false。

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

  1. 令 transaction 为this 的事务。

  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. 令 key 为 r。

  10. 令 r 为使用 primaryKey 将值转换为键的结果。重新抛出任何异常。

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

  12. 令 primaryKey 为 r。

  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. 令 request 为this 的请求。

  19. 将 request 的已处理标志设置为 false。

  20. 将 request 的完成标志设置为 false。

  21. 令 operation 为一个算法,用于使用当前 Realm 记录、this、key 和 primaryKey 运行迭代游标。

  22. 使用this 的源句柄、 operation 和 request 运行异步执行请求。

注意: 在加载新的游标数据之前多次调用此方法——例如,从 同一个 onsuccess 处理程序中调用 continuePrimaryKey() 两次——会导致第二次调用抛出一个 "InvalidStateError" DOMException, 因为游标的已获取值标志已被设置为 false。

如果在 只读事务中调用,以下方法会抛出一个 "ReadOnlyError" DOMException;如果 在事务并非活动状态时 调用,则会抛出一个 "TransactionInactiveError" DOMException。
request = cursor . update(value)

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

如果有效对象存储 使用内联键,并且键会发生更改,则抛出一个 "DataError" DOMException。

如果成功,request 的 result 将是该 记录的键。

request = cursor . delete()

删除游标指向的记录。

如果成功,request 的 result 将为 undefined。

update(value) 方法的步骤如下:

  1. 令 transaction 为this 的事务。

  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. 令 clone 为在 transaction 期间 targetRealm 中 value 的一个克隆。 重新抛出任何异常。

    为什么要创建值的副本? 值在存储时会被序列化。在这里将其视为副本 可使本规范中的其他算法将其视为 ECMAScript 值,但如果行为差异不可观察,实现可以对此进行 优化。
  9. 如果this 的有效 对象存储使用内联键,则:

    1. 令 kpk 为使用 clone 和this 的有效对象 存储的键路径 使用键路径从值中 提取键的结果。 重新抛出任何异常。

    2. 如果 kpk 为失败、无效,或不等于 this 的有效 键,则抛出 一个 "DataError" DOMException。

  10. 令 operation 为一个算法,用于使用this 的有效 对象存储、clone、this 的有效键和 false 运行将记录存储到对象存储。

  11. 返回使用this 和 operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: 将记录存储到对象存储的一个结果是,如果 自游标移动到该记录后,该记录已被删除, 则会创建一条新记录。

delete() 方法的步骤如下:

  1. 令 transaction 为this 的事务。

  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. 返回使用this 和 operation 运行异步执行请求的结果(一个 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();

  // 事件处理程序:
  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 为this 的作用域中对象存储的名称列表。

  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 为this 的 作用域中名称为 name 的对象 存储;如果不存在,则抛出 一个 "NotFoundError" DOMException。

  3. 返回一个与 store 和this 关联的对象 存储句柄。

注意: 在同一个 IDBTransaction 实例上 使用相同名称每次调用此方法都会返回同一个 IDBObjectStore 实例。

注意: 返回的 IDBObjectStore 实例特定于此 IDBTransaction。 如果在另一个 IDBTransaction 上调用此方法,则会返回另一个 IDBObjectStore 实例。

abort() 方法的步骤如下:

  1. 如果this 的状态为正在提交 或已完成, 则抛出一个 "InvalidStateError" DOMException。

  2. 使用this 和 null 运行中止 事务。

commit() 方法的步骤如下:

  1. 如果this 的状态不是活动, 则抛出一个 "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、 数据库 version 和 request 打开 数据库连接,运行以下步骤:

  1. 令 queue 为 storageKey 和 name 的连接队列。

  2. 将 request 添加到 queue。

  3. 等待 queue 中所有先前请求都被处理。

  4. 令 db 为 storageKey 中名称为 name 的数据库;如果不存在,则为 null。

  5. 如果 version 为 undefined,则当 db 为 null 时令 version 为 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,排入一个 数据库任务,以在 entry 上使用 db 的版本和 version 触发一个名为 versionchange 的版本更改事件。

      注意: 触发此事件可能会导致 openConnections 中的一个或多个其他 对象被关闭,在这种情况下,即使尚未在这些 对象上触发 versionchange 事件,也不会再触发。

    3. 等待所有事件都被触发。

    4. 如果 openConnections 中的任何连接仍未 关闭,则排入一个数据库任务,以在 request 上使用 db 的版本和 version 触发一个名为 blocked 的版本更改事件。

    5. 等待,直到 openConnections 中的所有连接都 关闭。

    6. 使用 connection、version 和 request 运行升级 数据库。

    7. 如果 connection 已被关闭, 则返回一个新创建的 "AbortError" DOMException 并中止这些步骤。

    8. 如果已设置 request 的错误,则使用 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,则在 connection 上触发 一个名为 close 的事件。

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

注意: 一旦一个连接的关闭 待处理标志被设置为 true,就不能再使用该连接 创建 新事务。所有创建 事务的方法都会先检查连接的 关闭 待处理标志, 并在其为 true 时抛出异常。

注意: 一旦连接关闭, 这可能解除升级数据库的步骤 和删除 数据库的步骤的阻塞,这两个步骤都会 等待与给定数据库的连接 关闭后才继续。

5.3. 删除数据库

要使用请求删除数据库的 storageKey、 数据库 name 和 request 删除 数据库,运行以下步骤:

  1. 令 queue 为 storageKey 和 name 的连接队列。

  2. 将 request 添加到 queue。

  3. 等待 queue 中所有先前请求都被处理。

  4. 如果存在,则令 db 为 storageKey 中名称为 name 的数据库。否则返回 0(零)。

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

  6. 对于 openConnections 中每个未将其 关闭待处理标志设置为 true 的 entry,排入 一个数据库任务,以在 entry 上使用 db 的版本 和 null 触发一个名为 versionchange 的版本更改事件。

    注意: 触发此事件可能会导致 openConnections 中的一个或多个其他对象 被关闭,在这种情况下,即使尚未在这些 对象上触发 versionchange 事件,也不会再触发。

  7. 等待所有事件都被触发。

  8. 如果 openConnections 中的任何连接仍未 关闭,则排入一个 数据库任务,以在 request 上使用 db 的 版本和 null 触发一个名为 blocked 的版本更改事件。

  9. 等待,直到 openConnections 中的所有连接都 关闭。

  10. 令 version 为 db 的版本。

  11. 删除 db。如果由于任何原因失败,则返回适当的 错误(例如一个 QuotaExceededError, 或一个 "UnknownError" DOMException)。

  12. 返回 version。

5.4. 提交事务

要使用要提交的 transaction 提交 事务,运行以下步骤:

  1. 将 transaction 的状态设置为正在提交。

  2. 并行运行以下步骤:

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

    2. 如果 transaction 的状态不再是正在提交, 则终止这些步骤。

    3. 尝试将 transaction 所做的任何未完成更改写入数据库, 同时考虑 transaction 的持久性提示。

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

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

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

      2. 将 transaction 的状态设置为已完成。

      3. 在 transaction 上触发一个名为 complete 的事件。

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

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

5.5. 中止事务

要使用要中止的 transaction 和 error 中止 事务,运行以下步骤:

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

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

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

    注意: 这会还原与 transaction 关联的所有连接、对象存储句柄和 索引句柄 实例的更改。

  4. 将 transaction 的状态设置为已完成。

  5. 将 transaction 的错误设置为 error。

  6. 对于 transaction 的请求列表中的每个 request, 中止针对 request 的异步执行请求步骤, 将 request 的已处理标志设置为 true, 并排入一个 数据库任务以运行以下步骤:

    1. 将 request 的完成标志设置为 true。

    2. 将 request 的结果设置为 undefined。

    3. 将 request 的错误设置为一个新 创建的 "AbortError" DOMException。

    4. 在 request 上触发一个名为 error 的事件, 并将其 bubbles 和 cancelable 属性初始化为 true。

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

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

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

    2. 在 transaction 上触发一个名为 abort 的事件, 并将其 bubbles 属性初始化为 true。

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

      1. 令 request 为与 transaction 关联的打开 请求。

      2. 将 request 的事务设置为 null。

      3. 将 request 的结果设置为 undefined。

      4. 将 request 的已处理标志设置为 false。

      5. 将 request 的完成标志设置为 false。

5.6. 异步执行请求

要使用 source 对象、要在数据库上执行的 operation 和一个可选的 request 异步执行请求,运行以下步骤:

如果所创建请求所属的事务使用 中止 事务的步骤被中止,则这些步骤可在任意时刻被中止。

  1. 令 transaction 为与 source 关联的事务。

  2. 断言: transaction 的状态为活动。

  3. 如果未给出 request,则令 request 为一个新的请求,其源为 source。

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

  5. 并行运行以下步骤:

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

    2. 令 result 为执行 operation 的结果。

    3. 如果 result 是一个错误,并且 transaction 的状态为正在提交, 则使用 transaction 和 result 运行中止 事务, 并终止这些步骤。

    4. 如果 result 是一个错误, 则还原 operation 所做的所有更改。

      注意: 这只会还原此请求所做的更改,而不会还原 事务所做的其他更改。

    5. 将 request 的已处理标志设置为 true。

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

      1. 从 transaction 的请求列表中移除 request。

      2. 将 request 的完成标志设置为 true。

      3. 如果 result 是一个错误,则:

        1. 将 request 的结果设置为 undefined。

        2. 将 request 的错误设置为 result。

        3. 在 request 上触发错误 事件。

      4. 否则:

        1. 将 request 的结果设置为 result。

        2. 将 request 的错误设置为 undefined。

        3. 在 request 上触发成功 事件。

  6. 返回 request。

5.7. 升级数据库

要使用 connection(一个连接)、一个新的 version 和 request 升级 数据库,运行以下步骤:

  1. 令 db 为 connection 的数据库。

  2. 令 transaction 为一个新的升级事务,并使用 connection 作为连接。

  3. 将 transaction 的作用域设置为 connection 的对象存储集合。

  4. 将 db 的升级事务设置为 transaction。

  5. 将 transaction 的状态设置为非活动。

  6. 启动 transaction。

    注意: 请注意,在此事务完成之前,不能打开与同一个 数据库的其他连接。

  7. 令 old version 为 db 的版本。

  8. 将 db 的版本设置为 version。此更改被 视为事务的一部分,因此如果 事务被中止,此更改将被还原。

  9. 将 request 的已处理标志设置为 true。

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

    1. 将 request 的结果设置为 connection。

    2. 将 request 的事务设置为 transaction。

    3. 将 request 的完成标志设置为 true。

    4. 将 transaction 的状态设置为活动。

    5. 令 didThrow 为在 request 上使用 old version 和 version 触发一个名为 upgradeneeded 的版本更改事件的结果。

    6. 如果 transaction 的状态为活动,则:

      1. 将 transaction 的状态设置为非活动。

      2. 如果 didThrow 为 true,则使用 transaction 和一个新 创建的 "AbortError" DOMException 运行中止 事务。

  11. 等待 transaction 完成。

    注意: 在事务的 生命周期期间调用的某些算法,例如提交 事务的步骤和中止事务的步骤, 包含特定于升级事务的步骤。

5.8. 中止升级事务

要使用 transaction 中止 升级事务,运行以下步骤:

注意: 这些步骤会根据需要由中止事务的步骤运行,后者会还原对数据库的更改,包括 关联对象 存储和索引的集合,以及 对版本的更改。

  1. 令 connection 为 transaction 的连接。

  2. 令 database 为 connection 的数据库。

  3. 如果 database 先前已存在,则将 connection 的版本设置为 database 的 版本; 如果 database 是新创建的,则设置为 0(零)。

    注意: 这会还原 IDBDatabase 对象所返回的 version 值。

  4. 如果 database 先前已存在,则将 connection 的对象存储集合设置为 database 中的对象存储集合; 如果 database 是新创建的,则设置为空集合。

    注意: 这会还原 IDBDatabase 对象所返回的 objectStoreNames 值。

  5. 对于与 transaction 关联的每个对象 存储句柄 handle,包括 transaction 期间创建或删除的对象存储对应的句柄:

    1. 如果 handle 的对象存储不是 在 transaction 期间新创建的,则将 handle 的 名称 设置为其 对象存储的名称。

    2. 将 handle 的索引集合设置为引用其对象存储的索引集合。

    注意: 这会还原相关 IDBObjectStore 对象所返回的 name 和 indexNames 值。

    这是如何可观察的? 虽然在事务 中止后,脚本无法通过在 IDBTransaction 实例上使用 objectStore() 方法访问对象存储,但脚本仍可能持有 IDBObjectStore 实例的引用,并可查询其中的 name 和 indexNames 属性。
  6. 对于与 transaction 关联的每个索引 句柄 handle, 包括 transaction 期间创建或删除的索引 对应的句柄:

    1. 如果 handle 的索引不是在 transaction 期间新创建的,则将 handle 的名称设置为 其索引的名称。

    注意: 这会还原相关 IDBIndex 对象所返回的 name 值。

    这是如何可观察的? 虽然在 事务中止后,脚本无法通过在 IDBObjectStore 实例上使用 index() 方法访问索引,但脚本仍可能持有 IDBIndex 实例的引用,并可查询其中的 name 属性。

注意: 即使被中止的升级事务正在 创建一个新数据库,IDBDatabase 实例的 name 属性也不会被修改。

5.9. 触发成功事件

要在 request 上触发成功 事件,运行以下步骤:

  1. 令 event 为使用 Event 创建 事件的结果。

  2. 将 event 的 type 属性设置为 "success"。

  3. 将 event 的 bubbles 和 cancelable 属性设置为 false。

  4. 令 transaction 为 request 的事务。

  5. 令 legacyOutputDidListenersThrowFlag 的初始值为 false。

  6. 如果 transaction 的状态为非活动, 则将 transaction 的状态设置为活动。

  7. 使用 legacyOutputDidListenersThrowFlag 在 request 上分派 event。

  8. 如果 transaction 的状态为活动, 则:

    1. 将 transaction 的状态设置为非活动。

    2. 如果 legacyOutputDidListenersThrowFlag 为 true, 则使用 transaction 和一个新创建的 "AbortError" DOMException 运行中止 事务。

    3. 如果 transaction 的请求列表为空, 则使用 transaction 运行提交 事务。

5.10. 触发错误事件

要在 request 上触发错误 事件,运行以下步骤:

  1. 令 event 为使用 Event 创建 事件的结果。

  2. 将 event 的 type 属性设置为 "error"。

  3. 将 event 的 bubbles 和 cancelable 属性设置为 true。

  4. 令 transaction 为 request 的事务。

  5. 令 legacyOutputDidListenersThrowFlag 的初始值为 false。

  6. 如果 transaction 的状态为非活动, 则将 transaction 的状态设置为活动。

  7. 使用 legacyOutputDidListenersThrowFlag 在request 上分派 event。

  8. 如果 transaction 的状态为活动, 则:

    1. 将 transaction 的状态设置为非活动。

    2. 如果 legacyOutputDidListenersThrowFlag 为 true, 则使用 transaction 和一个新创建的 "AbortError" DOMException 运行中止 事务并终止这些步骤。 即使 event 的已取消标志为 false,也会这样做。

      注意: 这意味着,如果触发错误事件并且任何事件 处理程序抛出异常,则 transaction 的 error 属性会被设置为一个 AbortError, 而不是 request 的错误,即使 从未调用 preventDefault()。

    3. 如果 event 的已取消标志为 false, 则使用 transaction 和request 的 错误运行中止 事务, 并终止这些步骤。

    4. 如果 transaction 的请求列表为空, 则使用 transaction 运行提交 事务。

5.11. 克隆值

要在 transaction 期间在 targetRealm 中创建 value 的一个克隆, 运行以下步骤:

  1. 断言: transaction 的状态为活动。

  2. 将 transaction 的状态设置为非活动。

    注意: 将事务 设置为非活动,这样由克隆操作触发的 getter 或其他副作用就无法针对该事务发出其他 请求。

  3. 令 serialized 为 ? StructuredSerializeForStorage(value)。

  4. 令 clone 为 ? StructuredDeserialize(serialized, targetRealm)。

  5. 将 transaction 的状态设置为活动。

  6. 返回 clone。

5.12. 创建用于检索多个项目的请求

要使用 targetRealm、sourceHandle、kind、queryOrOptions 和可选的 count 从一个对象存储或索引中创建一个用于检索多个项目的请求,运行以下步骤:

  1. 令 source 为来自 sourceHandle 的一个索引或对象存储。 如果 sourceHandle 是一个索引句柄,则 source 是该索引 句柄所关联的索引。 否则,source 是该对象存储句柄所关联的对象 存储。

  2. 如果 source 已被删除,则抛出一个 "InvalidStateError" DOMException。

  3. 如果 source 是一个索引,并且 source 的对象存储已被删除,则抛出 一个 "InvalidStateError" DOMException。

  4. 令 transaction 为 sourceHandle 的事务。

  5. 如果 transaction 的状态不是活动,则抛出一个 "TransactionInactiveError" DOMException。

  6. 令 range 为一个键 范围。

  7. 令 direction 为一个游标方向。

  8. 如果使用 queryOrOptions 运行是否为潜在有效键范围返回 true,则:

    1. 将 range 设置为使用 queryOrOptions 将值转换为键范围的结果。重新抛出任何异常。

    2. 将 direction 设置为 "next"。

  9. 否则:

    1. 将 range 设置为使用 queryOrOptions["query"] 将值转换为键范围的结果。 重新抛出任何异常。

    2. 将 count 设置为 queryOrOptions["count"]。

    3. 将 direction 设置为 queryOrOptions["direction"]。

  10. 令 operation 为一个要运行的算法。

  11. 如果 source 是一个索引,则将 operation 设置为使用 targetRealm、source、range、kind、 direction 和给定时的 count 从索引检索 多个项目。

  12. 否则将 operation 设置为使用 targetRealm、source、range、 kind、direction 和给定时的 count 从对象存储检索多个项目。

  13. 返回使用 sourceHandle 和 operation 运行异步执行请求的结果(一个 IDBRequest)。

注意: range 可以是一个键或键范围(一个 IDBKeyRange), 用于标识要检索的记录项目。如果为 null 或未给出,则使用一个无界键 范围。 如果指定了 count,并且范围内的记录多于 count 条,则只会 检索前 count 条。

6. 数据库操作

本节描述对 对象存储和索引中的数据执行的各种操作,这些对象位于数据库中。 这些操作由异步执行请求的步骤运行。

注意: 下面操作步骤中对StructuredDeserialize() 的调用可断言不会抛出异常(如! 前缀所示), 因为它们仅对 StructuredSerializeForStorage() 先前的输出进行操作。

6.1. 对象存储存储操作

要使用 store、value、一个可选的 key 和一个 no-overwrite flag 将记录存储到对象存储,运行 以下步骤:

  1. 如果 store 使用键 生成器,则:

    1. 如果 key 为 undefined,则:

      1. 令 key 为 为 store 生成键的结果。

      2. 如果 key 为失败,则此操作以一个 "ConstraintError" DOMException 失败。 中止此 算法且不再执行任何后续步骤。

      3. 如果 store 还使用内联键,则使用 value、key 和 store 的键路径运行 使用键路径将键注入 值。

    2. 否则,使用 key 为 store 运行可能更新键生成器。

  2. 如果向这些步骤给出了 no-overwrite flag 且其为 true,并且 store 中已经存在一条 记录,其 键等于 key,则此操作以一个 "ConstraintError" DOMException 失败。 中止此算法且不再执行任何后续步骤。

  3. 如果 store 中已经存在一条 记录, 其键等于 key,则使用 从对象存储删除记录从 store 中移除该记录。

  4. 在 store 中存储一条记录,以 key 作为其键,并以 ! StructuredSerializeForStorage(value) 作为其值。该记录存储在对象存储的 记录 列表中,使该列表按照记录的键以升序排列。

  5. 对于每个引用 store 的 index:

    1. 令 index key 为使用 value、index 的键路径和 index 的 multiEntry 标志使用键路径从值中提取键的结果。

    2. 如果 index key 是异常、无效或失败,则不再对 index 执行任何操作,并继续对下一个索引执行这些步骤。

      注意: 此步骤中抛出的异常不会被重新抛出。

    3. 如果 index 的multiEntry 标志为 false,或者 index key 不是一个数组 键,并且 index 已经包含一条 记录,其 键等于 index key,且 index 的唯一标志为 true,则此 操作以一个 "ConstraintError" DOMException 失败。 中止此 算法且不再执行任何后续步骤。

    4. 如果 index 的multiEntry 标志为 true,且 index key 是 一个数组键,并且 index 已经包含一条 记录,其 键等于 index key 的任意子键,且 index 的唯一标志为 true,则此操作以一个 "ConstraintError" DOMException 失败。 中止此算法且不再执行任何 后续步骤。

    5. 如果 index 的multiEntry 标志为 false,或者 index key 不是一个数组 键,则在 index 中存储一条记录, 以 index key 作为其键,并以 key 作为其值。该 记录存储在 index 的记录 列表中, 使该列表首先按记录的键排序, 然后按记录的值排序,均为升序。

    6. 如果 index 的multiEntry 标志为 true,且 index key 是 一个数组键,则 对 index key 的 子键中的每个 subkey,在 index 中存储一条记录, 以 subkey 作为其键,并以 key 作为其值。这些 记录存储在 index 的记录 列表中,使该列表首先按 记录的键排序,然后按记录的值排序,均为 升序。

      注意: 没有子键是有效的情况。在这种情况下, 不会向索引添加记录。

      注意: 即使子键中的任何成员 本身也是一个数组 键, 该成员也会直接用作索引记录的键。 嵌套的数组键 不会被展平或“解包”以 生成多行;只有最外层数组键会这样处理。

  6. 返回 key。

6.2. 对象存储检索操作

要使用 targetRealm、store 和 range 从对象存储检索值,运行以下步骤。 它们返回 undefined、一个 ECMAScript 值或一个错误(一个 DOMException):

  1. 如果存在,则令 record 为 store 的 记录 列表中第一条记录,其键位于 range 中。

  2. 如果未找到 record,则返回 undefined。

  3. 令 serialized 为 record 的值。如果从底层存储读取该值时发生错误,则返回一个新创建的 "NotReadableError" DOMException。

  4. 返回 ! StructuredDeserialize(serialized, targetRealm)。

要使用 store 和 range 从对象存储检索 键,运行以下步骤:

  1. 如果存在,则令 record 为 store 的 记录 列表中第一条记录,其键位于 range 中。

  2. 如果未找到 record,则返回 undefined。

  3. 返回使用 record 的键将键转换为值的结果。

要使用 targetRealm、store、range、kind、direction 和可选的 count 从对象存储检索多个项目,运行以下步骤:

  1. 如果未给出 count 或其为 0(零),则令 count 为无穷大。

  2. 令 records 为一个空的列表,用于存放记录。

  3. 如果 direction 为 "next" 或 "nextunique", 则将 records 设置为 store 的记录列表中键位于 range 中的前 count 条记录。

  4. 如果 direction 为 "prev" 或 "prevunique", 则将 records 设置为 store 的记录列表中键位于 range 中的后 count 条记录。

  5. 令 list 为一个空列表。

  6. 对于 records 中每个 record,根据 kind:

    "key"
    1. 令 key 为使用 record 的键将键转换为值的结果。

    2. 将 key 追加到 list。

    "value"
    1. 令 serialized 为 record 的值。

    2. 令 value 为 ! StructuredDeserialize(serialized, targetRealm)。

    3. 将 value 追加到 list。

    "record"
    1. 令 key 为 record 的键。

    2. 令 serialized 为 record 的值。

    3. 令 value 为 ! StructuredDeserialize(serialized, targetRealm)。

    4. 令 recordSnapshot 为一个新的记录快照, 将其键设置为 key,将值设置为 value,并将 主键设置为 key。

    5. 将 recordSnapshot 追加到 list。

  7. 返回 list。

6.3. 索引检索操作

要使用 targetRealm、index 和 range 从索引检索引用值,运行以下步骤:

  1. 如果存在,则令 record 为 index 的记录列表中第一条记录,其 键位于 range 中。

  2. 如果未找到 record,则返回 undefined。

  3. 令 serialized 为 record 的引用值。

  4. 返回 ! StructuredDeserialize(serialized, targetRealm)。

要使用 index 和 range 从索引检索 值,运行以下步骤:

  1. 如果存在,则令 record 为 index 的记录列表中第一条记录,其 键位于 range 中。

  2. 如果未找到 record,则返回 undefined。

  3. 返回使用 record 的值将键转换为值的结果。

要使用 targetRealm、index、range、kind、direction 和 可选的 count 从索引检索多个项目,运行以下步骤:

  1. 如果未给出 count 或其为 0(零),则令 count 为无穷大。

  2. 令 records 为一个空的列表,用于存放记录。

  3. 根据 direction:

    "next"
    1. 将 records 设置为 index 的记录列表中键位于 range 中的前 count 条记录。

    "nextunique"
    1. 令 rangeRecords 为一个列表,其中包含 index 的记录列表中键位于 range 中的记录。

    2. 令 rangeRecordsLength 为 rangeRecords 的大小。

    3. 令 i 为 0。

    4. 当 i 小于 rangeRecordsLength 时:

      1. 将 i 增加 1。

      2. 如果 record 的大小等于 count,则 跳出。

      3. 如果使用 |rangeRecords[i]| 和 |rangeRecords[i-1]| 中的键比较两个键的结果相等,则继续。

      4. 否则将 |rangeRecords[i]| 追加到 records。

    "prev"
    1. 将 records 设置为 index 的记录列表中键位于 range 中的后 count 条记录。

    "prevunique"
    1. 令 rangeRecords 为一个列表,其中包含 index 的记录列表中键位于 range 中的记录。

    2. 令 rangeRecordsLength 为 rangeRecords 的大小。

    3. 令 i 为 0。

    4. 当 i 小于 rangeRecordsLength 时:

      1. 将 i 增加 1。

      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. 令 serialized 为 record 的引用值。

    2. 令 value 为 ! StructuredDeserialize(serialized, targetRealm)。

    3. 将 value 追加到 list。

    "record"
    1. 令 index key 为 record 的键。

    2. 令 key 为 record 的值。

    3. 令 serialized 为 record 的引用值。

    4. 令 value 为 ! StructuredDeserialize(serialized, targetRealm)。

    5. 令 recordSnapshot 为一个新的记录快照, 将其键设置为 index key,将值设置为 value,并将 主键设置为 key。

    6. 将 recordSnapshot 追加到 list。

  6. 返回 list。

6.4. 对象存储删除操作

要使用 store 和 range 从对象存储删除 记录,运行以下步骤:

  1. 从 store 的记录 列表中移除所有键位于 range 中的记录(如果有)。

  2. 对于每个引用 store 的 index,从 index 的记录 列表中移除每条值位于 range 中的 记录(如果存在这样的记录)。

  3. 返回 undefined。

6.5. 记录计数操作

要使用 source 和 range 对范围中的 记录计数,运行以下步骤:

  1. 令 count 为 source 的记录列表中键位于 range 中的记录数量(如果有)。

  2. 返回 count。

6.6. 对象存储清空操作

要使用 store 清空对象 存储,运行以下步骤:

  1. 从 store 中移除所有记录。

  2. 在所有索引中, 若其引用 store,则移除所有 记录。

  3. 返回 undefined。

6.7. 游标迭代操作

要使用 targetRealm、cursor、要迭代到的可选 key 和 primaryKey,以及可选的 count 迭代 游标,运行以下 步骤:

  1. 令 source 为 cursor 的源。

  2. 令 direction 为 cursor 的方向。

  3. 断言:如果 给出了 primaryKey,则 source 是一个 索引, 并且 direction 为 "next" 或 "prev"。

  4. 令 records 为 source 中的记录列表。

    注意: records 始终按键以升序 排列。 当 source 是一个索引时,records 还会其次按值以升序排列 (其中索引中的值 是被引用 对象存储中记录的 键)。

  5. 令 range 为 cursor 的范围。

  6. 令 position 为 cursor 的位置。

  7. 令 object store position 为 cursor 的对象存储 位置。

  8. 如果未给出 count,则令 count 为 1。

  9. 当 count 大于 0 时:

    1. 根据 direction:

      "next"

      令 found record 为 records 中第一条满足以下所有要求的记录:

      • 如果定义了 key:

      • 如果定义了 primaryKey:

      • 如果定义了 position 且 source 是一个对象存储:

        • 记录的键大于 position。

      • 如果定义了 position 且 source 是一个索引:

        • 记录的键等 于 position,并且 记录的值大 于 object store position

        • 记录的键大于 position。

      • 记录的键位于 range 中。

      "nextunique"

      令 found record 为 records 中第一条满足以下所有要求的记录:

      • 如果定义了 key:

      • 如果定义了 position:

        • 记录的键大于 position。

      • 记录的键位于 range 中。

      "prev"

      令 found record 为 records 中最后一条满足以下所有要求的记录:

      • 如果定义了 key:

      • 如果定义了 primaryKey:

      • 如果定义了 position 且 source 是一个对象存储:

      • 如果定义了 position 且 source 是一个索引:

        • 记录的键等 于 position,并且 记录的值小 于 object store position

        • 记录的键小 于 position。

      • 记录的键位于 range 中。

      "prevunique"

      令 temp record 为 records 中最后一条满足以下所有要求的记录:

      • 如果定义了 key:

      • 如果定义了 position:

      • 记录的键位于 range 中。

      如果定义了 temp record,则令 found record 为 records 中第一条键等于 temp record 的键的记录。

      注意: 使用 "prevunique" 迭代时访问的记录与 "nextunique" 相同,但顺序相反。

    2. 如果未定义 found record,则:

      1. 将 cursor 的键 设置为 undefined。

      2. 如果 source 是一个索引,则将 cursor 的 对象存储位置设置为 undefined。

      3. 如果 cursor 的仅键标志为 false,则将 cursor 的 值设置为 undefined。

      4. 返回 null。

    3. 令 position 为 found record 的键。

    4. 如果 source 是一个索引,则令 object store position 为 found record 的值。

    5. 将 count 减少 1。

  10. 将 cursor 的位置设置为 position。

  11. 如果 source 是一个索引,则将 cursor 的对象存储 位置设置为 object store position。

  12. 将 cursor 的键设置为 found record 的键。

  13. 如果 cursor 的仅键标志为 false,则:

    1. 如果 source 是一个对象 存储,则令 serialized 为 found record 的值;否则为 found record 的引用 值。

    2. 将 cursor 的值设置为 ! StructuredDeserialize(serialized, targetRealm)

  14. 将 cursor 的已获取值标志设置为 true。

  15. 返回 cursor。

7. ECMAScript 绑定

本节定义如何将本规范中定义的键值 与 ECMAScript 值相互转换,以及如何使用键路径从 ECMAScript 值中提取键或将键注入 ECMAScript 值。本节引用 ECMAScript 语言规范中的类型和算法,并使用其中的一些 算法约定。 [ECMA-262] 此处未详细说明的转换定义于 [WEBIDL]。

7.1. 从值中提取键

要使用 value、keyPath 和一个可选的 multiEntry flag 使用键路径从值中提取键,运行 以下步骤。这些步骤的结果是一个键、 无效、 失败,或者这些步骤可能抛出异常。

  1. 令 r 为使用 value 和 keyPath 在值上计算键路径的结果。重新抛出任何 异常。

  2. 如果 r 为失败,则返回失败。

  3. 如果 multiEntry flag 为 false,则令 key 为使用 r 将值转换为键的结果;否则为使用 r 将值转换为 multiEntry 键的 结果。重新抛出任何异常。

  4. 如果 key 为 "invalid value" 或 "invalid type",则返回无效。

  5. 返回 key。

要使用 value 和 keyPath 在值上计算 键路径,运行以下步骤。这些步骤的结果是一个 ECMAScript 值或失败,或者这些步骤可能抛出异常。

  1. 如果 keyPath 是一个字符串列表,则:

    1. 令 result 为一个新的 Array 对象,就像由表达式 [] 创建一样。

    2. 令 i 为 0。

    3. 对于 keyPath 中的每个 item:

      1. 令 key 为使用 item 和 value 递归 在值上计算键路径的结果。

      2. 断言:key 不是突变完成。

      3. 如果 key 为失败,则中止整个算法并返回 失败。

      4. 令 p 为 ! ToString(i)。

      5. 令 status 为 CreateDataProperty(result, p, key)。

      6. 断言:status 为 true。

      7. 将 i 增加 1。

    4. 返回 result。

      注意: 这最多只会“递归”一层,因为键路径序列永远不能嵌套。

  2. 如果 keyPath 是空字符串,则返回 value 并跳过 剩余步骤。

  3. 令 identifiers 为在 U+002E FULL STOP 字符(.)处分割 keyPath 的严格分割结果。

  4. 对于 identifiers 中的每个 identifier, 跳转到下面相应的步骤:

    如果 Type(value) 为 String, 且 identifier 为 "length"

    令 value 为一个等于 value 中元素数量的 Number。

    如果 value 是一个 Array 且 identifier 为 "length"

    令 value 为 ! ToLength(! Get(value, "length"))。

    如果 value 是一个 Blob 且 identifier 为 "size"

    令 value 为一个等于 value 的 size 的 Number。

    如果 value 是一个 Blob 且 identifier 为 "type"

    令 value 为一个等于 value 的 type 的 String。

    如果 value 是一个 File 且 identifier 为 "name"

    令 value 为一个等于 value 的 name 的 String。

    如果 value 是一个 File 且 identifier 为 "lastModified"

    令 value 为一个等于 value 的 lastModified 的 Number。

    否则
    1. 如果 Type(value) 不是 Object,则返回失败。

    2. 令 hop 为 ! HasOwnProperty(value, identifier)。

    3. 如果 hop 为 false,则返回失败。

    4. 令 value 为 ! Get(value, identifier)。

    5. 如果 value 为 undefined,则返回失败。

  5. 断言: value 不是突变完成。

  6. 返回 value。

注意: 上述步骤中可以作出断言,因为此算法 仅应用于 StructuredDeserialize 的输出值, 并且只访问“自有”属性。

7.2. 将键注入值

注意: 本节中使用的键路径始终是字符串,而绝不会是 序列,因为不可能创建一个同时具有 键生成器和一个为 序列的键路径的对象存储。

要使用 value 和 keyPath 检查键是否可以注入 值,运行以下步骤。 这些步骤的结果为 true 或 false。

  1. 令 identifiers 为在 U+002E FULL STOP 字符(.)处分割 keyPath 的严格 分割结果。

  2. 断言: identifiers 不为空。

  3. 移除 identifiers 的最后一个项目。

  4. 对于 identifiers 中剩余的每个 identifier(如果有):

    1. 如果 value 既不是 Object 也不是 Array, 则返回 false。

    2. 令 hop 为 ! HasOwnProperty(value, identifier)。

    3. 如果 hop 为 false,则返回 true。

    4. 令 value 为 ! Get(value, identifier)。

  5. 如果 value 是 Object 或 Array, 则返回 true,否则返回 false。

注意: 上述步骤中可以作出断言,因为此算法 仅应用于 StructuredDeserialize 的输出值。

要使用 value、key 和 keyPath 使用键路径将键注入值,运行以下步骤:

  1. 令 identifiers 为在 U+002E FULL STOP 字符(.)处分割 keyPath 的严格 分割结果。

  2. 断言: identifiers 不为空。

  3. 令 last 为 identifiers 的最后一个项目,并将其从 列表中移除。

  4. 对于 identifiers 中剩余的每个 identifier:

    1. 断言: value 是一个 Object 或 Array。

    2. 令 hop 为 ! HasOwnProperty(value, identifier)。

    3. 如果 hop 为 false,则:

      1. 令 o 为一个新的 Object, 就像由表达式 ({}) 创建一样。

      2. 令 status 为 CreateDataProperty(value, identifier, o)。

      3. 断言:status 为 true。

    4. 令 value 为 ! Get(value, identifier)。

  5. 断言: value 是一个 Object 或 Array。

  6. 令 keyValue 为使用 key 将键转换为值的结果。

  7. 令 status 为 CreateDataProperty(value, last, keyValue)。

  8. 断言: status 为 true。

注意: 上述步骤中可以作出断言,因为此算法 仅应用于 StructuredDeserialize 的输出值, 并且已经运行了检查键是否可以注入 值的步骤。

7.3. 将键转换为值

要使用 key 将键 转换为值,运行以下步骤。 这些步骤返回一个 ECMAScript 值。

  1. 令 type 为 key 的类型。

  2. 令 value 为 key 的值。

  3. 根据 type:

    number

    返回一个等于 value 的 ECMAScript Number 值

    string

    返回一个等于 value 的 ECMAScript String 值

    date
    1. 令 date 为以 value 作为唯一参数执行 ECMAScript Date 构造函数的结果。

    2. 断言:date 不是突变 完成。

    3. 返回 date。

    binary
    1. 令 len 为 value 的长度。

    2. 令 buffer 为使用 len 执行 ECMAScript ArrayBuffer 构造函数的结果。

    3. 断言:buffer 不是突变 完成。

    4. 将 buffer 的 [[ArrayBufferData]] 内部槽中的条目设置为 value 中的条目。

    5. 返回 buffer。

    array
    1. 令 array 为不带参数执行 ECMAScript Array 构造函数的结果。

    2. 断言:array 不是突变 完成。

    3. 令 len 为 value 的大小。

    4. 令 index 为 0。

    5. 当 index 小于 len 时:

      1. 令 entry 为使用 value[index] 将键转换为 值的结果。

      2. 令 status 为 CreateDataProperty(array, index, entry)。

      3. 断言:status 为 true。

      4. 将 index 增加 1。

    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。

    如果 input 是一个 Date (具有 [[DateValue]] 内部槽)
    1. 令 ms 为 input 的 [[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 是一个Array exotic object
    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 为使用参数 entry 和 seen 将值转换为 键的结果。

      5. ReturnIfAbrupt(key)。

      6. 如果 key 为 "invalid value" 或 "invalid type",则中止这些步骤 并返回 "invalid value"。

      7. 将 key 追加到 keys。

      8. 将 index 增加 1。

    6. 返回一个新的数组键,其 值为 keys。

    否则

    返回 "invalid type"。

要使用一个 ECMAScript 值 input 将 值转换为 multiEntry 键,运行以下步骤。 这些步骤的结果是一个键,或 "invalid value",或 "invalid type",或者这些步骤可能抛出异常。

  1. 如果 input 是一个Array exotic object,则:

    1. 令 len 为 ? ToLength( ? Get(input, "length"))。

    2. 令 seen 为一个新的集合, 其中仅包含 input。

    3. 令 keys 为一个新的空列表。

    4. 令 index 为 0。

    5. 当 index 小于 len 时:

      1. 令 entry 为 Get(input, index)。

      2. 如果 entry 不是突变 完成,则:

        1. 令 key 为使用参数 entry 和 seen 将值转换为 键的结果。

        2. 如果 key 既不是 "invalid value"、"invalid type",也不是突变 完成, 且 keys 中没有项目等 于 key, 则将 key 追加到 keys。

      3. 将 index 增加 1。

    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 中支持 开发者将图像或音频等替代非文本内容存储为 Blob、 File 或 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" 与规范性文本区分开来, 如下所示:

这是一个信息性示例。

信息性注释以“Note”一词开头, 并使用 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. File API. 2024年12月4日. WD. URL: https://www.w3.org/TR/FileAPI/
[HTML]
Anne van Kesteren; et al. HTML 标准. 现行标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 现行标准. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. 用于 RFC 中 指示要求级别的关键词. 1997年3月. 最佳当前实践. URL: https://datatracker.ietf.org/doc/html/rfc2119
[STORAGE]
Anne van Kesteren. Storage 标准. 现行 标准. URL: https://storage.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 现行 标准. URL: https://webidl.spec.whatwg.org/

信息性参考文献

[Charmod-Norm]
Addison Phillips; et al. 万维网字符模型: 字符串匹配. 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 Storage(第二版). 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?