一文解惑:ES6的for...of 不能遍历Object对象? 您所在的位置:网站首页 js循环object对象 一文解惑:ES6的for...of 不能遍历Object对象?

一文解惑:ES6的for...of 不能遍历Object对象?

2023-12-01 06:47| 来源: 网络整理| 查看: 265

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

通过本篇文章,你可了解到以下知识点:

for of的常用用法 和其他循环的区别 不能遍历Object对象? 如何遍历Object对象? 关乎for of不能遍历的Iterator介绍 如何给Object增加Symbol.iterator方法

如果你想了解更详细的解说,请认真往下阅读;如果你想快速查看结论,请看『总结』目录。

前言

随着前端的不断发展,光循环就出现了好多种方法,for、forEach、do..while、for...in等等,不过这些循环也都各有各的应用场景和优缺点。

ES6又为我们提供了新的循环方法for...of,它可以循环字符串、数组及其他类数组对象,那作为最普遍存在的Object对象,按理,可以循环?

我们看一下下方的代码示例:

{ // 迭代数组   const iterable = ['a', 'b'];   for (const value of iterable) {     console.log(value);   }   // output: a b } { // 普通对象 const obj = { a: 'A', b: 'B' } for(const item of obj){ console.log(item) } // Uncaught TypeError: obj is not iterable }

oh no,报错了:Uncaught TypeError: obj is not iterable。提示obj是不可迭代的,显然直接用for...of去遍历Object对象是不行的。

本篇文章就带你深入了解一下,可以遍历大部分数据结构的for...of为何不能遍历Object对象。

for of的概念和常用用法

es6阮一峰介绍

概念

ES6引入for...of循环,作为遍历所有数据结构的统一的方法,创建一个循环来迭代可迭代的数据结构,包括数组、Set 和 Map 结构、某些类似数组的对象(比如arguments对象、DOM NodeList 对象)、Generator 对象,以及字符串。

一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法。

语法 variable:每个迭代的属性值被分配给该变量。 iterable:一个具有可枚举属性并且可以迭代的对象。 for (variable of iterable) { statement } 常用用法

数组

数组原生具备iterator接口(即默认部署了Symbol.iterator属性),for...of循环本质上就是调用这个接口产生的遍历器。

{   // 迭代数组   const iterable = ['a', 'b'];   for (const value of iterable) {     console.log(value);   }   // output: a b }

对比for...in循环,for...in循环只能获得对象的键名,不能直接获取键值。而ES6提供的for...of允许遍历获得键值。

const iterable = ['a', 'b', 'c', 'd']; for (let value in iterable) { // value为键名索引 console.log(value); } // output: 0 1 2 3 for (let value of iterable) { // value为键值,如需获取索引可以借助数组实例的`entries`方法和`keys`方法 console.log(value); } // output: a b c d

Set和Map结构

Set和Map结构介绍

Set 和 Map 结构也原生具有 Iterator 接口,可以直接使用for...of循环。

const iterable = new Set(["a", "b", "b", "c"]); for (let value of iterable) { console.log(value); } // output: a b c const iterable = new Map(); es6.set("a", 1); es6.set("b", 2); es6.set("c", 3); for (let [name, value] of iterable) { console.log(name + ": " + value); } // output: // a: 1 // b: 2 // c: 3

类数组对象

1. 字符串

// 字符串 let str = "hello"; for (let s of str) { console.log(s); // h e l l o }

2. DOM NodeList 对象

// DOM NodeList对象 let paras = document.querySelectorAll("p"); for (let p of paras) { p.classList.add("test"); }

3. arguments对象

// arguments对象 function printArgs() { for (let x of arguments) { console.log(x); } } printArgs('a', 'b'); // 'a' // 'b'

对象(不能使用for...of遍历)

// 普通对象 const obj = {   a: 'A',   b: 'B' } for(const item of obj){   console.log(item) } // Uncaught TypeError: obj is not iterable

我们发现,对于普通的对象,for...of结构不能直接使用,会报错,提示obj is not iterable,也就是说普通对象默认没有Iterator接口,必须部署了 Iterator 接口后才能使用。

和其他循环的区别 循环名称循环对象是否可中断循环是否有返回值forfor循环体的length可以无返回值forEach仅可循环数组、map、set等,不可循环字符串、普通对象不可以无返回值do...while满足某种条件,则可一直循环,至少循环一次可以无返回值while满足某种条件,则可一直循环可以无返回值map组成新的数组成员,仅可循环数组,不可循环字符串、普通对象,set、map不可中断返回新数组,不影响原数组filter过滤数组成员,仅可循环数组,不可循环字符串、普通对象,set、map不可中断返回新数组,不影响原数组for...in可循环数组、对象,不可循环map、set。可遍历数字键名,还可遍历手动添加的其他键,甚至包括原型链上的键可以无返回值for...of循环可迭代的对象,不可循环普通对象(统一数据结构遍历)可以无返回值 Array.prototype.newArr = () => {}; Array.prototype.anotherNewArr = () => {}; const array = ['foo', 'bar', 'baz']; for (const value in array) {  console.log(value); // 0 1 2 newArr anotherNewArr } for (const value of array) { console.log(value); // 'foo', 'bar', 'baz' } 不能遍历Object对象?

在for...of介绍章节,我们提到了一个可迭代数据结构。那什么是可迭代数据结构呢?

实际上,任何具有 Symbol.iterator 属性的数据结构都是可迭代的。

当用for...of迭代普通对象时,会报obj is not iterable。也就是说普通对象是不可迭代的,没有Symbol.iterator属性。

我们查看几个可被for...of迭代的对象原型,看看和普通对象有何不同: Set数据结构

image.png

Array数组

image.png

Object对象

image.png

可以看到,这些可被for of迭代的对象,都实现了一个Symbol(Symbol.iterator)方法,而普通对象没有这个方法。

简单来说,for...of 语句创建一个循环来迭代可迭代的对象,可迭代的对象内部实现了Symbol.iterator方法,而普通对象没有实现这一方法,所以普通对象是不可迭代的。

关乎for of不能遍历的Iterator介绍

Iterator ES6阮一峰介绍

ES6 为了统一集合类型数据结构的处理,增加了 iterator 接口,供 for...of 使用,简化了不同结构数据的处理。而 iterator 的遍历过程,则是类似 Generator 的方式,迭代时不断调用next方法,返回一个包含value(值)和done属性(标识是否遍历结束)的对象。

如何遍历Object对象

通过前面的用法示例,我们知道,for...of可以迭代数组、Map等数据结构,顺着这个思路,我们可以结合对象的Object.values()、Object.keys()、Object.entries()方法以及解构赋值的知识来用for...of遍历普通对象。

1. Object扩展方法values()、keys()、entries()

Object.values()返回普通对象的键值组成的数组

Object.keys()返回普通对象的键名组成的数组

Object.entries()返回普通对象的键名键值组成的二维数组

const obj = {   a: 'A',   b: 'B' } // Object.values() console.log(Object.values(obj)) // ["A", "B"] // Object.keys() console.log(Object.keys(obj)) // ["a", "b"]\ // Object.entries() console.log(Object.entries(obj)) // [["a","A"],["b","B"]] for (let [key, value] of Object.entries(obj)) { console.log(key, value); } // a A // b B

2. Generator 函数重新包装对象

const obj = { a: 1, b: 2, c: 3 } function* entries(obj) { for (let key of Object.keys(obj)) { yield [key, obj[key]]; } } for (let [key, value] of entries(obj)) { console.log(key, '->', value); } // a -> 1 // b -> 2 // c -> 3 Object增加Symbol.iterator方法

给普通对象增加Symbol.iterator方法,不用担心[Symbol.iterator]属性会被Object.keys()获取到导致遍历结果出错,因为Symbol.iterator这样的Symbol属性,需要通过Object.getOwnPropertySymbols(obj)才能获取,Object.getOwnPropertySymbols() 方法返回一个给定对象自身的所有 Symbol 属性的数组。

// 普通对象 const obj = {   a: 'A',   b: 'B',   [Symbol.iterator]() {     // 这里Object.keys不会获取到Symbol.iterator属性     const keys = Object.keys(obj);     let index = 0;     return {       next: () => {         if (index 


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有