在 JavaScript 中,可迭代对象应该是可重复迭代的吗?

2024-02-26

我发现有些iterable可以重复迭代:

const iterable = {
  [Symbol.iterator]: function* () {
    yield 1;
    yield 3;
    yield 5;
  }
}

console.log([...iterable]);
console.log([...iterable]);
console.log([...iterable]);

虽然有些人不能:

function* generatorFn() {
  yield 1;
  yield 3;
  yield 5;
}

const iterable = generatorFn();

console.log([...iterable]);
console.log([...iterable]);
console.log([...iterable]);

是否存在一个可迭代对象应该或不应该重复可迭代的规则?

我理解为什么他们的行为不同(这是因为第二种情况,当iterable[Symbol.iterator]调用函数时,返回相同的迭代器(即iterable本身。可以尝试iterable[Symbol.iterator]() === iterable它会返回true. iterable.next也是一个函数。所以在这种情况下,iterable是一个生成器对象、一个可迭代对象和一个迭代器,三者皆有)。但我想知道可迭代作为一种对象类型,是否有一个明确定义的行为来确定它是否应该重复可迭代。)


好的,我想我应该总结一下我们在评论中学到的一些东西,并添加更多内容,然后通过写下您的具体问题的答案来结束。

[...x] syntax

The [...x]语法适用于支持的事物iterables界面。并且,要支持可迭代接口,您所要做的就是支持Symbol.iterator属性来提供一个函数(当调用时)返回一个迭代器。

内置迭代器也是可迭代的

Javascript 中内置的所有迭代器都派生自相同的迭代器IteratorPrototype https://www.ecma-international.org/ecma-262/10.0/index.html#sec-%25iteratorprototype%25-object。不需要迭代器执行此操作,这是内置迭代器所做的选择。

这个内置IteratorPrototype也是一个 Iterable。它支持Symbol.iterator属性是一个函数,它的作用就是return this。这是按规格 https://www.ecma-international.org/ecma-262/10.0/index.html#sec-%25iteratorprototype%25-@@iterator.

这意味着所有内置迭代器,例如someSet.values()将与[...x]句法。我不确定为什么这非常有用,但它肯定会导致人们对 Iterable 可以做什么和 Iterator 可以做什么感到困惑,因为这些内置迭代器可以表现为任何一个。

它会导致一些奇怪的行为,因为如果你这样做:

let s = new Set([1,2,3]);
let iter = s.values();    // gets an iterator
let x = [...iter];
let y = [...iter];
console.log(x);
console.log(y);

第二[...iter]是一个空数组,因为这里只有一个迭代器。实际上,x === y。因此第一个let x = [...iter];耗尽迭代器。它坐在done并且无法再次迭代集合。这是因为内置迭代器有这种奇怪的行为,它们的行为就像一个可迭代的,但只是return this。他们不会创建一个可以再次迭代集合的新迭代器,就像使用实际可迭代集合时一样。每次访问时,这个可迭代集合都会返回一个全新的迭代器s[Symbol.iterator]()如下所示:

let s = new Set([1,2,3]);
let x = [...s];
let y = [...s];
console.log(x);
console.log(y);

普通迭代器不适用于[...x]

要成为迭代器,您需要实现的只是支持.next()方法并用适当的对象进行响应。事实上,这是一个符合规范的超级简单迭代器:

const iter = { 
    i: 1, 
    next: function() { 
        if (this.i <= 3) {
            return { value: this.i++, done: false }; 
        } else {
            return { value: undefined, done: true }; 
        } 
    }
}

如果你尝试做let x = [...iter];,它会抛出这个错误:

TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))

但是,如果你通过添加适当的内容使其成为可迭代的[Symbol.iterator]它的属性,它将作为[...iter];

const iter = { 
    i: 1, 
    next: function() { 
        if (this.i <= 3) {
            return { value: this.i++, done: false }; 
        } else {
            return { value: undefined, done: true }; 
        } 
    },
    [Symbol.iterator]: function() { return this; }
}

let x = [...iter];
console.log(x);

然后,它可以作为[...iter]因为它现在也是一个可迭代的。

发电机

Generator 函数在被调用时返回一个 Generator 对象。每spec https://www.ecma-international.org/ecma-262/10.0/index.html#sec-generator-objects,该 Generator 对象的行为既是Iterator and an Iterable。故意没有办法判断这个 Iterator/Iterable 是否来自生成器,这显然是故意做的 https://stackoverflow.com/a/19660350/816620。调用代码只知道它是一个Iterator/Iterable生成器函数只是创建对调用代码透明的序列的一种方法。它的迭代就像任何其他迭代器一样。


两个迭代器的故事

在您原来的问题中,您显示了两个迭代器,一个可以重复工作,另一个则不能。这里有两件事在起作用。

首先,一些迭代器“消耗”它们的序列,并且没有办法重复迭代相同的序列。这些将是制造序列,而不是静态集合。

其次,在您的第一个代码示例中:

const iterable = {
  [Symbol.iterator]: function* () {
    yield 1;
    yield 3;
    yield 5;
  }
}

console.log([...iterable]);
console.log([...iterable]);
console.log([...iterable]);

单独的迭代器

该可迭代对象是一个可迭代对象。它不是迭代器。您可以通过调用来请求迭代器iterable[Symbol.iterator]()这是什么[...iterable]做。但是,当您这样做时,它会返回一个全新的 Generator 对象,它是一个全新的迭代器。每次你打电话iterable[Symbol.iterator]()或导致被调用[...iterable],你会得到一个新的、不同的迭代器。

你可以在这里看到:

    const iterable = {
      [Symbol.iterator]: function* () {
        yield 1;
        yield 3;
        yield 5;
      }
    }

    let iterA = iterable[Symbol.iterator]();
    let iterB = iterable[Symbol.iterator]();
    
    // shows false, separate iterators on separate generator objects
    console.log(iterA === iterB);      

因此,您正在使用每个迭代器创建一个全新的序列。它新调用生成器函数来获取新的生成器对象。

相同的迭代器

但是,用你的第二个例子:

function* generatorFn() {
  yield 1;
  yield 3;
  yield 5;
}

const iterable = generatorFn();

console.log([...iterable]);
console.log([...iterable]);
console.log([...iterable]);

这不一样。你叫什么iterable这就是我认为的pseudo-iterable。它实现了IterableIterator接口,但是当你要求它提供一个Iterator like [...iterable]确实如此,它每次都返回相同的对象(本身)。所以,每次你这样做[...iterable],它在同一个迭代器上运行。但是该迭代器已耗尽并且位于done第一次执行后的状态[...iterable]。所以,后两个[...iterable]是空数组。迭代器没有什么可提供的了。

你的问题

是否存在一个可迭代对象应该或不应该重复可迭代的规则?

并不真地。首先,给定的迭代器最终到达done状态(非无限迭代器)一旦到达就完成给出任何结果done状态。根据迭代器的定义。

所以,无论是否Iterable表示某种静态序列可以重复迭代取决于是否Iterator当询问迭代器时它提供的内容每次被询问时都是新的且唯一的,我们在上面的两个示例中看到,Iterable可以选择任何一种方式。

它每次都可以生成一个新的、唯一的迭代器,每次都通过序列呈现新的迭代。

Or, an Iterable可以产生完全相同的Iterator每一次。如果这样做的话,一旦该迭代器到达done状态,它被卡在那里。

还要记住,某些 Iterables 表示可能不可重复的动态集合/序列。对于像这样的事情来说,情况并非如此Set or a Map,但是更多自定义类型的 Iterables 可能在迭代时本质上“消耗”它们的集合,并且当它完成时,即使您获得一个新的迭代器,也不会再有更多的集合了。

想象一下,一个迭代器向您提供一个价值 1 美元到 10 美元之间的随机金额的代码,并在您每次向迭代器询问下一个值时从您的银行余额中减去该代码。在某个时候,您的银行余额会达到$0并且该迭代器已经完成,即使获得一个新的迭代器仍然必须处理相同的问题$0银行余额(没有更多值)。这将是一个迭代器的例子 “消耗”值或某些资源并且不可重复。

但我想知道可迭代作为一种对象类型,是否有一个明确定义的行为来确定它是否应该重复可迭代。

不。它是特定于实现的,完全取决于您要迭代的内容。使用像这样的静态集合Set or a Map or an Array,您可以获取一个新的迭代器并每次生成一个新的迭代。但是,我所说的psuedo-iterable(每次请求时返回相同迭代器的可迭代对象)或迭代时序列被“消耗”的可迭代对象可能无法重复迭代。因此,它可以故意采用任何一种方式。没有标准的方法。这取决于正在迭代的内容。

测试你所拥有的

这里有一些有用的测试,可以帮助人们稍微理解一些事情:

// could do a more comprehensive test by calling `obj.next()` to see if
// it returns an appropriate object with appropriate properties, but
// that is destructive to the iterator (consumes that value) 
// so we keep this one non-destructive
function isLikeAnIterator(obj) {
    return typeof obj === "object" && typeof obj.next === "function)";
}

function isIterable(obj) {
    if (typeof obj === "object" && typeof obj[Symbol.iterator] === "function") {
        let iter = obj[Symbol.iterator]();
        return isLikeAnIterator(iter);
    }
    return false;
}

// A pseudo-iterable returns the same iterator each time
// Sometimes, the pseudo-iterable returns itself as the iterator too
function isPseudoIterable(obj) {
   if (isIterable(obj) {
       let iterA = obj[Symbol.iterator]();
       if (iterA === this) {
          return true;
       }
       let iterB = obj[Symbol.iterator]();
       return iterA === iterB;
   }
   return false;
}

function isGeneratorObject(obj) {
    if (!isIterable(obj) !! !isLikeAnIterator(obj) {
        // does not meet the requirements of a generator object
        // which must be both an iterable and an iterator
        return false;
    }
    throw new Error("Can't tell if it's a generator object or not by design");
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 JavaScript 中,可迭代对象应该是可重复迭代的吗? 的相关文章

  • 暂停除了已激活的玩家之外的所有其他玩家。

    我有这个插件 它可以将不同的样式应用于 html5
  • ReferenceError:regeneratorRuntime未定义(但在范围内工作)

    我遇到过这种奇怪的情况 ReferenceError regeneratorRuntime is not defined 我已经设法在一个非常小的设置中重现 与同一问题上的类似问题相比 并且还注意到一些奇怪的行为 具体取决于是否使用范围 以
  • 如何使用 JavaScript 创建链接?

    我有一个标题字符串和一个链接字符串 我不知道如何将两者放在一起以使用 JavaScript 在页面上创建链接 任何帮助表示赞赏 我试图解决这个问题的原因是因为我有一个 RSS 源并且有一个标题和 URL 列表 我想将标题链接到 URL 以使
  • JavaScript 中的埃拉托斯特尼筛法对大量数据无限运行

    我一直在尝试写埃拉托斯特尼筛法 http en wikipedia org wiki Sieve of EratosthenesJavaScript 中的算法 基本上我只是按照以下步骤操作 创建从 2 到 n 1 的连续整数列表 令第一个素
  • Eslint errorring 导入没有扩展名的 jsx

    我正在尝试在 es6 中导入 jsx 文件而不需要 jsx 扩展名 import LoginErrorDialog from LoginErrorDialogView Not import LoginErrorDialog from Log
  • Chrome 中的性能问题

    我目前正在从事一个相对较大的项目 使用 AngularJs 构建 应用程序的一部分是一个表单 您可以向其中添加任意数量的页面 不幸的是 添加了很多不必要的垃圾 即表示表单模型的对象可能会变得非常大 在某些时候 Chrome 基本上无法处理它
  • 如何格式化 Highcharts 的 (x,y) 对数据的日期时间

    我的序列化方法会产生如下所示的日期时间字符串 2014 07 09T12 30 41Z 为什么下面的代码不起作用 function container highcharts xAxis type datetime series data x
  • 图像无法在带有 DOM 的 IE 中加载:控制台中的 7009 错误(无法解码)

    当在 IE 中的单个页面上加载许多图像时 在 IE11 中重现 其中一些图像开始加载失败 并在控制台中出现类似以下警告的内容 DOM7009 无法解码 URL 处的图像 某些唯一的 url 当我查看网络流量时 似乎确实从服务器收到了每个图像
  • 带有淘汰赛js的隐形recaptcha

    我正在完成隐形验证码 但我在实现它时遇到问题 谷歌开发人员页面中的代码显示它应该是这样的
  • 防止 iOS 键盘在 cordova 3.5 中滚动页面

    我正在使用 Cordova 3 5 和 jQuery mobile 构建 iOS 应用程序 我在大部分应用程序中禁用了滚动功能 但是 当我选择输入字段时 iOS 键盘会打开并向上滚动页面 我不想要这个功能 由于输入足够高 键盘不会覆盖它 我
  • 调整图像大小并将画布旋转 90 度

    这里有很多关于在 js 上使用画布旋转图像的主题 我阅读了其中的大部分内容 但无法找到解决我的问题的方法 我正在接收任何分辨率的图像 来自上传组件 我将其大小调整为 1024x768 如下所示 var canvas document cre
  • 尝试将数据存储在点击器网站中

    我正在尝试存储一个名为的变量score无论何时刷新 您都会一次又一次地使用它 我不明白的是它的代码是什么 我尝试了一些方法 但似乎都不起作用 这是我的答题器网站 但是当我尝试使用 JavaScript 来存储它时 它不起作用window o
  • Chrome 扩展程序在代码中使用 client_secret

    我正在开发具有自己的 oAuth 授权的 Google Chrome 扩展 当然 我必须使用 client id 和 client secret 作为请求令牌 有什么办法可以向用户隐藏这些数据吗 由于此请求只是 javascript 源代码
  • 如何制作没有 ng-repeat 的模板并使用 Angular-drag-and-drop-lists 将数据传递到 $scope?

    我想用角度拖放列表 https github com marceljuenemann angular drag and drop lists使用我自己的网格模板到所见即所得编辑器 如何构建我自己的 HTML 模板而不需要ng repeat因
  • 在 HTML5 画布中,如何用我选择的背景遮盖图像?

    我试图用画布来实现这一点 globalCompositeOperation 但没有运气 所以我在这里问 这里有类似的问题 但我没有在其中找到我的案例 我的画布区域中有图层 从下到上的绘制顺序 画布底座填充纯白色 fff 用fillRect
  • 在移动设备上滚动

    这个问题更多的是一个建议研究 我确实希望它对其他人有帮助 并且它不会关闭 因为我不太确定在哪里寻求有关此事的建议 在过去的 6 个月里 我一直在进行移动开发 我有机会处理各种设备上的各种情况和错误 最麻烦的是滚动问题 当涉及到在网站的多个区
  • 带参数的事件监听器

    我想将参数传递给 JavaScript 中的事件侦听器 我已经找到了解决方案 但我无法理解它们为什么或如何工作以及为什么其他解决方案不起作用 我有 C C 背景 但是 Javascript 函数的执行有很大不同 您能否帮助我理解以下示例如何
  • 用于交互式图形绘制的轻量级 JavaScript 库? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有兴趣了解用于绘制交互式图表的最轻量级 javascript 库 我掌握的数据主要是与海洋研究相关的科学数据 我知道一些 jquery
  • Jquery - 选择选项后如何获取选项的特定数据类型?

    我将直接跳到标记 然后解释我想要做什么 HTML 选择选项
  • 如何在执行新操作时取消先前操作的执行?

    我有一个动作创建器 它会进行昂贵的计算 并在每次用户输入内容时调度一个动作 基本上是实时更新 但是 如果用户输入多个内容 我不希望之前昂贵的计算完全运行 理想情况下 我希望能够取消执行先前的计算并只执行当前的计算 没有内置功能可以取消Pro

随机推荐

  • Android Studio无法登录GitHub

    我在 Android Studio 上登录 GitHub 时遇到此问题 无法登录 无法获取用户信息 404 未找到 有人遇到过这个问题吗 我该如何解决这个问题 我也遇到过这个问题 尝试使用令牌登录 在浏览器上登录github com 转到设
  • vb6 调用形式,名称包含在数组中

    我有一个名为菜单的数组 它包含每个元素的表单名称 我怎样才能动态地调用它们 例如 如果菜单 1 登录 and 菜单 2 注销 我需要说 Login Show 但我想使用数组名称来执行此操作 我显然不能这样做 Menus X Show 这在V
  • 找不到语言 json 的内核 - 原子上的氢

    我发现有些人抱怨python kernel但这不是我的问题 这是错误的打印屏幕 https i stack imgur com DGvfC png 您可以在其中看到当我尝试运行 ipynb 文件时 它给我一个错误json kernel 该包
  • android EMFILE(打开文件太多)

    我实现了一个文件缓存来加载小图像以实现长网格视图 滚动一段时间后 我得到了很多libcore io ErrnoException open failed EMFILE Too many open files 我该如何避免这种情况 这是读取一
  • CodeDomProvider:LIB环境变量位于哪里?

    我正在使用一个System CodeDom Compiler CodeDomProvider CreateProvider CSharp 动态编译 C 类 最近 我从 Visual Studio 2010 切换到 Vistual Studi
  • Woot-badge 就像 PHP 中的一样

    有谁知道如何在用 PHP 编写的网站中实现某些功能 类似于堆栈溢出上的 Woot badge 和 Fanatic badge 我想奖励我的用户 如果他们连续 75 天每天访问我的网站 没有一天不访问它 我的网站使用会话进行登录 我使用 My
  • Google Maps Android API 实用程序集群管理器在创建集群之前是否有最低数量的标记?

    I am using the Google Maps Android API Utility Library to enable clustering in my app When five or more markers are co l
  • NSSpeechRecognizer 示例

    好的 所以我需要这样做 等待命令 晚安 然后运行一个动作 有人可以解释如何实现这一点吗 试试这个网站 http www cocoadev com index pl NSSpeechRecognizer http www cocoadev c
  • R 中带有 rCharts 的 nvd3 scatterPlot:增加标签的字体大小?

    我正在尝试增加使用创建的图中 x 轴和 y 轴的字体大小 NVD3 和 rCharts 这是我的情节代码 任何帮助表示赞赏 n1 lt nPlot pValues Chr data dat type scatterChart height
  • Git rebase 更改作者? [复制]

    这个问题在这里已经有答案了 所以我愚蠢地在一台未配置 git 的机器上进行了 3 次提交 没有作者或电子邮件 并且我想将这 3 次提交 尚未推送 作者更改为他们应该的内容 I know git commit amend可以换作者 但是怎么才
  • Android-按下按钮时将文本视图添加到布局

    现在我有一个文本字段 其下方有一个按钮 add 我想做到这一点 以便每次在文本字段中输入文本并按下 添加 按钮时 都会将一个新的文本视图添加到其下方的垂直布局中 并包含用户在该字段中键入的文本 我不想简单地使文本视图不可见 然后在单击时可见
  • Java 线程示例? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 谁能给出一个示例程序 以简单的方式解
  • BaseAdapter 类不会在 Asynctask 中 setAdapter - Android

    我有一个收集用户名 评论和数字的 asynctask 它将它们放入字符串中 然后调用 BaseAdapter 类 创建一个适配器 并将适配器设置为该类 但我的代码不起作用 它使应用程序崩溃 这是我的代码 public class Dashb
  • 试图根据具体情况抑制覆盖是错误的吗? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我明白为什么通过方法重写实现的多态性非常有用 我问的是 在某些情况下 当多态对象作为参数被接收时 而不是在定义其类时 尝试抑制它可能会出现什么问
  • 如何将 OpenSSL 密钥文件导入 Windows 证书存储区

    我有 OpenSSL 生成的 PEM 格式的 X 509 证书及其关联的密钥文件 连接到原型服务器时需要此证书进行身份验证 这在 Linux 上运行良好 我一直在使用 Microsoft SChannel API 在 Windows 平台上
  • MVC 中的点击计数器

    我正在尝试在 ASP NET MVC 中为网站的不同部分构建一个点击计数器 知道执行此操作的最佳位置在哪里 或者有一个好的方法吗 我们想要跟踪网站各个部分的点击量 根据您想要的粒度 以下是一些选项 您可以实施一个动作过滤器属性 http m
  • Android 设备监视器已禁用

    我刚刚开始 Android 开发并正在设置安卓工作室 http developer android com sdk index html在我的Mac上 按照说明here http vimeo com 113893631 当尝试运行一个简单的
  • Android 意图有时会“处理”而不是 ACTION_SEND

    我的应用程序应该处理共享文本 例如来自亚马逊应用程序的 URL 因此 我将以下意图过滤器添加到我的主要活动中
  • React Native - 将道具从一个屏幕传递到另一个屏幕(使用选项卡导航器进行导航)

    我需要将数据从主屏幕传递到第二屏幕 如果我单击主屏幕上的按钮导航到 SecondScreen 有大量示例说明如何执行此操作 但找不到任何显示如何传递到 SecondScreen 如果我使用 v2 底部选项卡导航器 的内容从主屏幕到第二屏幕
  • 在 JavaScript 中,可迭代对象应该是可重复迭代的吗?

    我发现有些iterable可以重复迭代 const iterable Symbol iterator function yield 1 yield 3 yield 5 console log iterable console log ite