JS 中可迭代和可枚举有什么区别?我正在经历 For/of 和 For/In 循环,这些术语经常出现

Mah*_*ash 10 javascript iterable enumerable for-in-loop for-of-loop

我在研究 For/in 和 For/of 循环时遇到术语 Iterable 和 Enumerable。对象应该是可枚举的,我们必须使用 For/in 循环来循环对象的属性,并使用 For/of 来循环数组和字符串。我无法理解这两个术语。这两者有什么区别?

Utm*_*tor 11

有几件事是与众不同的。

\n

关于可迭代的一些知识:

\n
    \n
  • 可迭代对象是数组的概括。这个概念允许我们使任何对象在 for..of 循环中可用;
  • \n
  • iterable 是一个接口,它指定如果对象实现了关键是 [symbol.iterator] link的方法,则该对象可以被访问。
  • \n
\n

关于枚举的一些信息:

\n
    \n
  • 它只是意味着如果您使用 for..in 循环或 Object.keys 迭代对象,该属性将会显示;
  • \n
  • JavaScript 中的可枚举属性意味着如果使用 for\xe2\x80\xa6in 循环或 Object.keys() 方法迭代某个属性,则可以查看该属性。默认情况下,通过简单赋值或属性初始值设定项创建的所有属性都是可枚举的。
  • \n
\n
    \n
  1. 可枚举 [for in] 查看对象内部的属性,而不是值 [仅在其中enumerable: true- 默认情况下适用于所有 props];
  2. \n
  3. 可迭代 [for of] 查看值;
  4. \n
\n

更深入一点:

\n
\n

迭代器是附加到数组的另一个对象,它告诉其他函数如何访问其中的所有不同值。\n有数组、字符串、NodeList、Sets、Map,它们有内置迭代器,但对象没有没有它。

\n
\n

默认情况下该对象是不可迭代的,但您可以实现它。

\n

所以你可以使用

\n
    \n
  • for .. of用于[array, Map, Set, String]迭代值;
  • \n
  • for .. in让数组迭代一个键;
  • \n
  • for .. in让对象枚举其(对象)属性;
  • \n
  • 循环NodeList
  • \n
\n

请查看此处的示例或使用提供的沙箱链接。\n同一示例的沙箱链接。

\n

\r\n
\r\n
let arr = [\'value1\', \'value2\', \'value3\'];\n\nlet obj = {\n  propName1: \'propValue1\',\n  propName2: \'propValue2\',\n  propName3: \'propValue3\'\n};\n\nconsole.log(\'=====================WORKING WITH ARRAYS===================\');\nconsole.log(\'For Of \')\nfor (const value of arr) {\n  console.log(\'value: \', value);\n}\n\nconsole.log(\'For In\');\nfor (const key in arr) {\n  console.log(\'key: \', key, \' value: \', arr[key]);\n}\n\nconsole.log(\'=====================WORKING WITH OBJECTS===================\');\nconsole.log(\'For In:\');\nfor (const prop in obj) {\n  console.log(\'prop: \', prop, \'value: \', obj[prop]);\n}\n\nObject.defineProperty(obj, "definedPropEnFalse", {\n  value: \'value of definedPropEnFalse\',\n  enumerable: false,\n});\n\nObject.defineProperty(obj, "definedPropEnTrue", {\n  value: \'value of definedPropEnTrue\',\n  enumerable: true,\n});\n\nconsole.log(\'For In for Objects with enumerables:\');\nfor (const prop in obj) {\n  console.log(\'prop: \', prop, \'value: \', obj[prop]);\n}\n\nconsole.log(\'For In for Objects with Object.keys and forEach:\');\nObject.keys(obj).forEach(e => console.log(`key=${e}  value=${obj[e]}`));\n\n\nconsole.log(\'=====================WORKING WITH STRINGS===================\');\nlet str = "Go Over A String"\nconsole.log(\'Using For Of for String:\');\nfor (const char of str) {\n  console.log(char);\n}\n\n\nconsole.log(\'=====================WORKING WITH Sets===================\');\nconsole.log("Looping over a Set");\nlet testSet = new Set();\ntestSet.add(\'Hello\');\ntestSet.add(\'Hope\');\ntestSet.add(\'You are getting it xD\');\n\nfor (const setItem of testSet) {\n  console.log(setItem);\n}\n\n\nconsole.log(\'=====================WORKING WITH Maps===================\');\nconsole.log(\'Iterate over Map using For of\')\nvar myMap = new Map();\nmyMap.set("0", "foo");\nmyMap.set(1, "bar");\nmyMap.set({}, "baz");\n\nfor (const [key, value] of myMap.entries()) {\n  console.log(key, value);\n}
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n


\n

你可能会问我该如何记住呢?- 简单的!

\n

助记符:

\n
    \n
  • \' o \'f -> 不是 \' o \'对象;
  • \n
  • \' i \'n -> 不是\' i \'terables。
  • \n
\n

另一个助记符:

\n
    \n
  • for..in..keys===外键=== 用于for...in键;
  • \n
  • for...of对于价值观
  • \n
\n

in给你索引。

\n

摘自这篇文章的评论

\n
\n

如果一个对象从技术上来说是一个数组,但代表了某个东西的集合(列表、集合),那么for..of循环它是一个很好的语法。

\n

  • “*所以你可以使用 for .. in 来迭代一个键;*” - 不,[请不要!](/sf/ask/35035311/) (3认同)

VLA*_*LAZ 5

Iterable 适用于

值可以是可迭代的,也可以是不可迭代的。它需要实现众所周知的符号@@iterator@@asyncIterator。实现的方法还必须通过返回迭代器来满足可迭代协议。有了这些,就可以通过将这些值视为可以迭代的东西来与这些值进行特殊的交互(因此称为“可迭代”)。这里有些例子:

for...of

可迭代对象最基本也可能最常见的用途是迭代它们。该for...of循环将执行此操作并从迭代器中获取项目,直到没有剩余为止。

细绳:

const str = "hello world";

for (const char of str)
  console.log(char);
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { max-height: 100% !important; }
Run Code Online (Sandbox Code Playgroud)

大批

const arr = ["a", "b", "c", "d"];

for (const item of arr)
  console.log(item);
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { max-height: 100% !important; }
Run Code Online (Sandbox Code Playgroud)

自定义对象

const iterable = {
  [Symbol.iterator]() {
    let repeat = 0;
    return {
      next() {
        return {
          value: 42, 
          done: repeat++ >= 3
        };
      }
    }
  }
}

for (const item of iterable)
  console.log(item);
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { max-height: 100% !important; }
Run Code Online (Sandbox Code Playgroud)

扩展语法...

当传播值时,会使用迭代器,并且您会为来自该迭代器的每个值获得一些东西。例如,扩展到数组中将[...value]创建一个包含所有值的数组。传播到函数调用中fn(...value)将以每个项目作为参数来调用该函数。

细绳

const str = "hello world";

console.log([...str]); //spread into array
console.log(...str);   //spread into function call
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { max-height: 100% !important; }
Run Code Online (Sandbox Code Playgroud)

大批

const arr = ["a", "b", "c", "d"];

console.log([...arr]); //spread into array
console.log(...arr);   //spread into function call
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { max-height: 100% !important; }
Run Code Online (Sandbox Code Playgroud)

自定义对象

const iterable = {
  [Symbol.iterator]() {
    let repeat = 0;
    return {
      next() {
        return {
          value: 42, 
          done: repeat++ >= 3
        };
      }
    }
  }
}

console.log([...iterable]); //spread into array
console.log(...iterable);   //spread into function call
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { max-height: 100% !important; }
Run Code Online (Sandbox Code Playgroud)

数组解构

这个名字可能有点误导。数组解构总是使用对象的迭代器。这并不意味着它只能用于数组。

细绳

const str = "hello world";

const [first, second] = str;
console.log(first, second);
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { max-height: 100% !important; }
Run Code Online (Sandbox Code Playgroud)

大批

const arr = ["a", "b", "c", "d"];

const [first, second] = arr;
console.log(first, second);
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { max-height: 100% !important; }
Run Code Online (Sandbox Code Playgroud)

自定义对象

const iterable = {
  [Symbol.iterator]() {
    let repeat = 0;
    return {
      next() {
        return {
          value: 42, 
          done: repeat++ >= 3
        };
      }
    }
  }
}

const [first, second] = iterable;
console.log(first, second);
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { max-height: 100% !important; }
Run Code Online (Sandbox Code Playgroud)

可枚举用于对象属性

只有对象属性可以是可枚举的。没有任何价值。这可以通过使用Object.defineProperty()Object.defineProperties()Reflect.defineProperty()或进行配置Object.create()

不可枚举对象属性

很难获得详尽的列表,但这传达了这样的想法:不可枚举属性被排除在属性的某些“批量”操作之外。

然而,不可枚举属性仍然可以直接访问。它们不是“隐藏的”或“私有的”,只是没有以最常见的机制出现来获取所有属性。

const obj = Object.defineProperties({}, {
  "a": { value: 1, enumerable: true},
  "b": { value: 2, enumerable: false},
  "c": { value: 3, enumerable: true},
});

for (const prop in obj)
  console.log("for...in:", prop); //a, c
  
console.log("Object.keys():", Object.keys(obj));     // [ "a", "c" ]
console.log("Object.values():", Object.values(obj)); // [ 1, 3 ]

const clone1 = {...obj};
console.log("clone1:", clone1);               // { "a": 1, "c": 3 }
console.log('"b" in clone1:', "b" in clone1); // false
console.log("clone1.b:", clone1.b);           // undefined

const clone2 = Object.assign({}, obj);
console.log("clone2:", clone2);               // { "a": 1, "c": 3 }
console.log('"b" in clone2:', "b" in clone2); // false
console.log("clone2.b:", clone2.b);           // undefined

//still accessible
console.log('"b" in obj:', "b" in obj); // true
console.log("obj.b:", obj.b);           // 2
Run Code Online (Sandbox Code Playgroud)
.as-console-wrapper { max-height: 100% !important; }
Run Code Online (Sandbox Code Playgroud)

还有一些机制允许查看不可枚举的属性:Object.getOwnPropertyNames()例如Object.getOwnPropertyDescriptors()能够显示它们。