JS中for...in 和 for...of 的区别解析

1. 迭代的对象不同

  • for … in 用于迭代对象的可枚举字符串属性,包括自身属性继承的属性,但不会遍历对象的原型链上的 非可枚举属性,以及对象的方法。

从内置构造函数(如 Array 和 Object)创建的对象会从 Array.prototype 和 Object.prototype 继承不可枚举属性,例如 Array 的 indexOf() 方法或 Object 的 toString() 方法,它们在 for…in 循环中不会被访问到。

  • for … of 用于迭代可迭代对象定义的要进行迭代的值。可迭代对象 包括 数组、字符串、Set、Map等,还包括 arguments 对象。它遍历的是可迭代对象的迭代器(Iterator)返回的值或键值对,而不能直接用于普通的对象。

当 for…of 循环迭代一个可迭代对象时,它首先调用可迭代对象的 @@iterator 方法,该方法返回一个迭代器,然后重复调用生成器的 next() 方法,以生成要分配给 每次循环迭代的 可迭代对象 的值的序列。

注意: 每次迭代都会创建一个新的变量。在循环体内部重新赋值变量不会影响可迭代对象的原始值。

要成为可迭代对象,必须要有一个迭代器 @@iterator方法,也就是说这个对象必须有一个键为@@iterator的属性,通过常量 Symbol.iterator 访问这个属性,并返回一个迭代器对象

迭代器对象包含一个 next() 方法,无参数或者接受一个参数的函数,并返回符合 IteratorResult 接口的对象。
next() 方法会返回一个具有 valuedone 属性的对象。

  • value 表示当前迭代的值。如果迭代器已经到达末尾,那么 value 的值通常为 undefined,但这并不是必然的,具体取决于迭代器的实现。
  • done 是一个布尔值,表示迭代是否已完成。迭代器是否已完成遍历。当遍历结束时,done 为 true,否则为 false。

实际上,两者都不是严格要求的;如果返回没有任何属性的对象,则实际上等价于 { done: false, value: undefined }

const obj = {
  a: 1,
  b: { h:123 },
};
//定义在自身的属性
Object.defineProperty(obj, 'c', {
  value: 3,
});
//定义在自身的属性
Object.defineProperty(obj, 'd', {
  value: 4,
  enumerable: true,//是否可枚举,默认false不可枚举
});
//定义在原型上的属性
Object.prototype.e=5;
for (let key in obj) {
  console.log(key); // 输出 'a' 、 'b'、 'd'、'e',但不会输出 'c'、'h'
}
console.log(Object.keys(obj)); // 输出 ['a', 'b','d'],不包括 'c'、'e'、'h'
console.log(Object.getOwnPropertyNames(obj)); // 输出 ['a', 'b', 'c','d'],包括 'c', 但不包括'e'、'h'
扩展:

Object.keys 会返回一个包含所有可枚举自有字符串属性的数组,
Object.getOwnPropertyNames 则会包含所有属性,包括不可枚举的。
Object.getOwnPropertyDescriptor(obj, prop)静态方法返回一个对象,该对象描述给定对象上特定属性(即直接存在于对象上而不在对象的原型链中的属性)的配置。

obj
要查找其属性的对象。
prop
要检索其描述的属性的名称或 Symbol。
返回值
如果指定的属性存在于对象上,则返回其属性描述符,否则返回 undefined。
// 对象本身的属性的属性描述符
const desc = Object.getOwnPropertyDescriptor(obj, "c");
console.log(desc);
// {
//	value: 3, 
// 	writable: false,
//	enumerable: false, 
// 	configurable: false
// }
// 对象原型上的属性的属性描述符
const desc1 = Object.getOwnPropertyDescriptor(Object.prototype, "e");
console.log(desc1);
//{
//	value: 5, 
//	writable: true, 
//	enumerable: true, 
//	configurable: true
// }

属性描述符

  • value
与属性关联的值(仅限数据描述符)。
  • writable :是否可更改
当且仅当与属性关联的值可以更改时,为 true(仅限数据描述符)。
  • enumerable : 是否可枚举
当且仅当此属性在相应对象的属性枚举中出现时,为 true。
  • configurable :是否可删除
当且仅当此属性描述符的类型可以更改且该属性可以从相应对象中删除时,为 true。

在迭代 Array 时,for…of 循环和 for…in 循环之间的区别。

Object.prototype.objCustom = function () {};
Array.prototype.arrCustom = function () {};
const iterable = [3, 5, 7];
iterable.foo = "hello";
for (const i in iterable) {
  console.log(i);
}
// "0"、"1"、"2"、"foo"、"arrCustom"、"objCustom"
for (const i in iterable) {
  if (Object.hasOwn(iterable, i)) {
    console.log(i);
  }
}
// "0" "1" "2" "foo"
for (const i of iterable) {
  console.log(i);
}
// 3 5 7

使用 Object.hasOwn() 来检查找到的可枚举属性是否为对象的自有属性,即非继承属性。

const arr=['A','B', ,'D', ,'F'];
  for(const key in arr){
    console.log(key); //0,1,3,5
  }
  for(const item of arr){
    console.log(item); // A,B,undefined,D,undefined,F
  }

for…in 使用属性枚举而不是数组的迭代器。在稀疏数组中,for…of 会访问空槽,但 for…in 不会访问空槽。

2. 遍历顺序

  • for … in 循环遍历对象属性时,遍历的顺序是不确定的,因为对象属性没有固定的顺序。

根据现代 ECMAScript 规范的定义,遍历的顺序是一致且可预测的。在原型链的每个组件中,所有非负整数键(可以作为数组索引)将首先按值升序遍历,然后是其他字符串键按属性创建的先后顺序升序遍历。

  • for … of 循环遍历可迭代对象时,遍历的顺序是按照对象的迭代器定义的顺序进行的。

3. 可迭代性要求

  • for … in 循环不需要对象满足可迭代性要求。
  • for … of 循环要求被遍历的对象必须是可迭代对象(实现了迭代器接口)。如果对象没有迭代器接口,尝试使用 for … of 循环会抛出错误。

总的来说:

  • for…in 循环用于迭代对象的可枚举字符串属性,包括自身属性继承的属性,
  • for…of 循环用于迭代可迭代对象定义的要进行迭代的值

到此这篇关于JS中for...in 和 for...of 的区别的文章就介绍到这了,更多相关js for...in 和 for...of 区别内容请搜索代码部落以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码部落!

本文章来源于网络,作者是:fighting ~,由代码部落进行采编,如涉及侵权请联系删除!转载请注明出处:https://daimabuluo.cc/JavaScript/2320.html

联系我们

在线咨询:点击这里给我发消息

邮件:dick@daimabuluo.cc

遇到问题?请给我们留言

请填写您的邮箱地址,我们将回复您的电子邮件