For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/proposal-typedarray-findwithin.md.
  • 简体中文
  • TypedArray Find Within S1

    中文标题:TypedArray 内部查找

    提案概览
    提案速览

    该提案向 TypedArray.prototype 添加子序列搜索方法(search、searchLast、contains),解决 TypedArray 中缺乏原生高效子序列搜索的问题。它定义了 needle 类型、可选的位置参数,并将算法实现留给引擎。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    TypedArray 内部查找

    ECMAScript 提案,用于在 TypedArray 中搜索子序列

    该提案目前处于流程第 1 阶段

    问题

    ECMAScript 应为 TypedArray 提供一种原生的 indexOf 类型方法,用于搜索元素子序列。

    如今使用 TypedArray,可以获取特定单个元素的索引,但没有机制来高效定位元素序列。子序列搜索在服务器端应用程序(如 Node.js)中已经常见了一段时间,通过 Buffer 对象对 Uint8Array.prototype.indexOf 方法的覆盖实现,但这在 Web 上的 TypedArray 中并未普遍支持,这迫使应用程序实现缓慢的替代方案,通常依赖于对数组进行非优化的线性搜索。

    function findSubsequence(haystack, needle) {
      if (needle.length === 0) return 0;
      if (needle.length > haystack.length) return -1;
      
      outer: for (let i = 0; i <= haystack.length - needle.length; i++) {
        for (let j = 0; j < needle.length; j++) {
          if (haystack[i + j] !== needle[j]) continue outer;
        }
        return i;
      }
      return -1;
    }
    
    // 适用于任何 TypedArray
    const uint8 = new Uint8Array([1, 2, 3, 4, 5]);
    const int16 = new Int16Array([1, 2, 3, 4, 5]);
    console.log(findSubsequence(uint8, new Uint8Array([3, 4]))); // 2
    console.log(findSubsequence(int16, new Int16Array([3, 4]))); // 2

    提案

    该提案是向 TypedArray.prototype 添加 API,以支持三种形式的优化子序列搜索:search 返回第一次出现的起始索引,searchLast 返回最后一次出现的起始索引,contains 如果子序列存在则返回简单的布尔值 true/false。所有三个方法都接受可选的 position 参数来控制搜索的起始位置。对于 searchcontains,仅考虑从 position 或之后开始的匹配。对于 searchLast,仅考虑从 position 或之前开始的匹配。

    const enc = new TextEncoder();
    const u8 = enc.encode('Hello TC39, Hello TC39');
    
    console.log(u8.search(enc.encode('TC39'))); // 6
    console.log(u8.search(enc.encode('TC39'), 7)); // 17
    console.log(u8.searchLast(enc.encode('TC39'))); // 17
    console.log(u8.searchLast(enc.encode('TC39'), 16)); // 6
    console.log(u8.contains(enc.encode('TC39'))); // true
    console.log(u8.contains(enc.encode('TC39'), 18)); // false

    具体如何实现子序列搜索算法打算留作实现细节。

    needle 类型

    needle 参数可以是:

    • TypedArray(相同或不同的元素类型)——通过其 @@iterator 方法进行迭代。每个生成的值必须是与 haystack 类型正确的值(对于非 BigInt TypedArray 为 Number,对于 BigInt TypedArray 为 BigInt);如果任何值类型错误,搜索返回 -1。这会创建 needle 元素的快照,这对于当 needle 由 SharedArrayBuffer 支持时的正确性是必要的。
    • 可迭代对象(除 String 外)——其元素被收集并针对 haystack 的元素类型进行类型检查。如果任何元素类型错误,搜索返回 -1
    • 字符串——抛出 TypeError。尽管字符串是可迭代的,但其迭代产生码点,这可能不是搜索 TypedArray 时的预期行为。
    • 任何其他值——抛出 TypeError
    const u8 = new Uint8Array([1, 2, 3, 4, 5]);
    
    // 相同类型的 TypedArray
    u8.search(new Uint8Array([3, 4])); // 2
    
    // 可迭代对象(例如普通数组)
    u8.search([3, 4]); // 2
    
    // 不同类型的 TypedArray(通过 @@iterator 迭代)
    u8.search(new Int16Array([3, 4])); // 2
    
    // 字符串抛出 TypeError
    u8.search('hello'); // TypeError
    
    // 不可迭代对象抛出 TypeError
    u8.search(42); // TypeError

    跨类型浮点精度

    当 needle TypedArray 的浮点类型比 haystack 更窄时,通过更窄类型往返可能丢失精度,导致匹配失败。Needle 元素通过 @@iterator 作为 JavaScript 数字读回,当存储在 Float32Array 中被舍入的值将不会与 Float64Array 中更高精度的表示进行 SameValueZero 匹配。

    const f64 = new Float64Array([0.3]);
    
    // Float32 无法精确表示 0.3 — 它四舍五入为 ≈0.30000001192092896
    f64.search(new Float32Array([0.3]));  // -1(无匹配)
    
    // 在 Float32 中精确的值(整数、2 的幂等)工作正常
    const f64b = new Float64Array([0.25, 0.5, 42]);
    f64b.search(new Float32Array([0.25]));  // 0
    f64b.search(new Float32Array([42]));    // 2

    这不是此提案特有的问题——它是 IEEE 754 浮点运算的固有属性,并且同样适用于任何跨类型元素比较。

    为什么只针对 TypedArray?为什么不是所有 可迭代对象

    该提案通常可以解决在任何可迭代对象中搜索子序列的相同问题。这是委员会应该决定的事情。然而,那里有几个问题:

    • haystack TypedArray 中专门优化搜索 needle 的性能比一般处理可迭代协议更容易。虽然该提案处理可迭代对象可能有意义,但该方法中有一组不同的性能和优化路径考虑因素。
    • TypedArray 在成员元素上是同质的,字符串也是如此。然而,其他类型的可迭代对象可能产生各种类型的值。虽然可迭代对象总是产生相同类型的值是最常见的,但并非要求如此。这也使得难以在一般情况下进行优化。