ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深入 TypeScript 的 `for...of`:告别 `for...in` 数组陷阱,理解编译降级与迭代边界

深入 TypeScript 的 `for...of`:告别 `for...in` 数组陷阱,理解编译降级与迭代边界 深入 TypeScript 的for...of告别for...in数组陷阱理解编译降级与迭代边界【免费下载链接】typescript-book:books: The definitive guide to TypeScript and possibly the best TypeScript book :book:. Free and Open Source 项目地址: https://gitcode.com/gh_mirrors/ty/typescript-bookfor...of是 TypeScript以及 ES6中最常用的数组遍历语法之一但在旧版编译目标下它有着明确的实现与类型限制。本篇基于 TypeScript 手册仓库typescript-book的 for...of 章节 及其配套示例代码 code/es6/for..of.ts系统讲解for...in的经典陷阱、for...of的正确用法、面向 ES5 的降级产物以及它在字符串、NodeList等类型上的边界帮助你写出意图清晰、行为正确的迭代代码。为什么需要for...offor...in的数组陷阱初学 JavaScript 的开发者最常犯的错误之一就是对数组使用for...in它并不会按直觉遍历数组的元素而是遍历传入对象的键key。也就是说对数组而言它遍历的是下标0, 1, 2而不是值9, 2, 5var someArray [9, 2, 5]; for (var item in someArray) { console.log(item); // 输出 0,1,2下标而不是 9,2,5 }如果你期望得到9, 2, 5实际得到的却是0, 1, 2。这正是for...of在 TypeScript 和 ES6 中诞生的原因之一。仓库的配套示例 code/es6/for..of.ts 中模块m0与m1恰好并排呈现了这一对比m0使用for...in遍历数组m1使用for...of方便读者直接对照验证两种语法的输出差异。用for...of正确遍历数组元素for...of会直接遍历集合中的成员值下面的代码会按预期依次输出9, 2, 5var someArray [9, 2, 5]; for (var item of someArray) { console.log(item); // 9,2,5 }这里item依次被赋值为数组中的每一个元素而不是它的下标。for...of让循环的意图一目了然——我要遍历这个数组的每一个成员而不是我要以某个步长访问这个对象的某些属性。逐字符遍历字符串除了数组for...of还可以让 TypeScript 轻松地逐字符遍历字符串无需手动split或按索引访问var hello is it me youre looking for?; for (var char of hello) { console.log(char); // 逐个字符输出i, s, 空格, i, t ... }char会依次取到字符串中的每一个字符。这在处理字符级逻辑如统计、转换、逐字渲染时非常直观。JS 生成面向非 ES6 目标的降级对于 ES6 之前的编译目标TypeScript 会把for...of降级downlevel为标准的下标递增循环for (var i 0; i list.length; i)。以上一节数组示例为例编译产物如下var someArray [9, 2, 5]; for (var item of someArray) { console.log(item); } // 编译后变为 // for (var _i 0; _i someArray.length; _i) { var item someArray[_i]; console.log(item); }你可以看到for...of在表达意图上更清晰同时显著减少了需要书写的代码量以及需要绞尽脑汁想出的循环变量名——编译器自动生成了_i这样的临时下标。仓库中的编译产物完整印证了文档中的这一描述对比 code/es6/for..of.ts 与其生成的 code/es6/for..of.js内容相同的还有 code/es6/forof.js可以看到m1模块中的for...of被原样转换成了for (var _i 0; _i someArray.length; _i) { var item someArray[_i]; ... }字符串遍历模块m2也采用相同的hello.lengthhello[_i]模式。这一降级之所以发生是因为该目录下的 code/es6/tsconfig.json 是空对象{}完全使用编译器的默认配置——在那个时期的 TypeScript 中默认编译目标即 ES5因此for...of一律走降级路径。限制仅适用于数组与字符串旧编译目标如果你没有将编译目标设为 ES6 或更高版本那么生成的代码会假定被遍历的对象存在length属性并且可以通过数字下标访问例如obj[2]。因此在这些旧版 JavaScript 引擎上for...of只对string和array受支持。一旦 TypeScript 发现你遍历的对象既不是数组也不是字符串就会给出明确的错误信息is not an array type or a string type。文档中的经典例子是querySelectorAll返回的NodeListlet articleParagraphs document.querySelectorAll(article p); // Error: Nodelist is not an array type or a string type for (let paragraph of articleParagraphs) { paragraph.classList.add(read); }配套源码 code/es6/for..of.ts 的第二个m2模块TypeScript 允许同名模块声明合并原样保留了这段代码与错误注释。一个值得注意的细节是查看生成的 code/es6/for..of.js你会发现尽管类型检查报告了错误编译器依然为这段代码生成了降级后的for (var _i 0; _i articleParagraphs.length; _i)循环——这印证了当时的 TypeScript 默认在存在类型错误时仍会输出产物除非开启noEmitOnError类型错误是检查期的提示而非阻断编译的硬性约束。因此使用for...of时要遵循一条原则只对你能确认是数组或字符串的对象使用它。文档同时指出这一限制可能在未来的 TypeScript 版本中被移除例如后续版本引入对Symbol.iterator可迭代协议的原生支持后for...of的适用范围得到了扩展。与可迭代协议Iterable/Iterator的关系for...of之所以能遍历集合底层依赖的是 ES6 的可迭代协议。在本书的下一章 Iterators 迭代器 中可以看到只要一个对象实现了带[Symbol.iterator]方法的Iterable接口for...of就能直接驱动它例如文档中的Frame组件容器let frame new Frame(Door, [new Component(top), new Component(bottom), new Component(left), new Component(right)]); for (let cmp of frame) { console.log(cmp); }迭代器接口本身由next(): IteratorResultT组成而IteratorResult是{ done: boolean; value: T }的二元组同时实现next与[Symbol.iterator]返回自身的IterableIterator则既支持手动next()调用也支持for...of循环。典型的无限序列——斐波那契数列——也可以用IterableIteratornumber实现并直接交给for...of消费。关于编译目标还有一个实用技巧详见 Iterators 迭代器 末尾上述基于Symbol.iterator的代码通常需要 ES6 编译目标但只要目标运行环境本身支持Symbol.iterator也可以采用ES5 编译目标 引入 ES6 lib如es6.d.ts的方式编译通过产物在 Node 4、Google Chrome 等环境可用。仓库中的 code/es6/iterators.ts 正是这一做法的落地示例它在 ES5 目标下以自定义next(): {done: boolean, value?: Component}的形式实现迭代注释中明确说明示例在目标为 ES5 且添加es6.d.ts后可在现代浏览器和 Node 中运行。小结与实战建议你可能会惊讶于自己在代码中遍历数组的次数有多频繁。下一次需要遍历数组时请优先考虑for...of语义正确遍历的是成员值而非下标规避for...in的数组陷阱意图清晰读代码的人一眼就能看出你在遍历集合元素代码更少不需要手写下标变量、长度判断与索引访问编译器会替你处理降级细节注意边界在旧编译目标下它只适用于数组和字符串遍历NodeList等集合类型时要么先转换成数组如Array.from要么换用带下标的传统循环。在本书中for...of被编排在 Future JavaScript NowSUMMARY.md一章之下紧接其后的是 Iterators 迭代器 一章。掌握好这两者你就能在 TypeScript 中自信地处理绝大多数集合遍历场景也让审查你代码的人更愉快。【免费下载链接】typescript-book:books: The definitive guide to TypeScript and possibly the best TypeScript book :book:. Free and Open Source 项目地址: https://gitcode.com/gh_mirrors/ty/typescript-book创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表