详解JS对象遍历的顺序问题

可能有些同学听过在 JavaScript 中遍历对象顺序不固定的这一说法。事实上,这个说法不是特别准确。

对待遍历顺序,对象有一套自己既定的规则,在此规则下呢,对象的遍历顺序会受插入元素顺序的影响,但是不完全受插入元素先后顺序的影响。如果您有「必须按插入元素顺序遍历」的场景,可以考虑使用 Map。
遍历对象的方法有很多种,我们经常会使用的有 for...in ,除此之外,还有:

  • Object.keys
  • Object.entries
  • Obejct.getOwnerProPertyNames
  • Reflect.ownKeys
  • ......

上面我们列的几个方法,都按照一样的规则去遍历对象。而实际的遍历规则会根据 key 值类型的不同而不同。
在一个对象中,如果我们的 key 值是像 '1'、'200'这种正整数格式的字符串。 遍历的顺序是按照 key 值的大小来排列的。
比如我们看这样的一个例子:

const obj = {}

obj['10'] = 'a';
obj['9'] = 'b';
obj[8] = 'c';
obj[7] = 'd';

console.log(Object.keys(obj)) //  ["7", "8", "9", "10"]

我们最后的遍历顺序完全忽视了插入顺序,并且,值得我们注意的是,在对象中,就算我们添加属性时的索引值是 Number 类型,最后的结果还是会被隐式的转为字符串。

数组作为对象的一种,也符合上面的规则,又或许,有上面的表现就是因为要兼容数组的缘故呢。除此之外,通过上面的规则,我们还可以推断出,对类数组(key 值是正整数且有 length 属性)进行遍历也是按照索引顺序的。
另外,如果我们的 key 值是不能转为正整数的字符串,这其中包括了可以转换为负数的字符串( 如 '-1' )、小数格式的字符串(如 '1.0' ) 和其他的字符串。他们的遍历顺序会比较符合直觉,就是插入对象的顺序:

const obj2 = {}

obj2['1.1'] = 'a';
obj2['1.0'] = 'b';
obj2['-1'] = 'c';
obj2['jack'] = 'd'

console.log(Object.keys(obj2)); //  ["1.1", "1.0", "-1", "jack"]

事实上,对象的索引值的类型不仅可以是字符串,还可以是 Symbol 类型。对于 Symbol 类型而言,它的遍历顺序也是单纯的按照插入对象的顺序。

如果我们的对象综合了上面所有的情况,即一个对象的索引值出现了所有的类型(各种形式的字符串、Symbol 类型),它会:

  • 先按照我们上面提的关于正整数的规则遍历正整数部分
  • 按接下来会插入顺序遍历剩下的字符串
  • 最后再按照插入顺序遍历 Symbol 类型

相信到这里,大家已经完全明白了对象的遍历顺序问题,最后还有一点值得大家注意的点,是 for...in 的遍历顺序问题。

最开始的时候,for...in 的遍历顺序并没有一个统一的标准,浏览器厂商会按照他们的喜好去设置 for...in 的遍历顺序。如果您对遍历顺序有要求并且要兼容老的浏览器版本,建议不使用它。后来 ES 2019 的 一个提案 对此现象进行了规范,现在 for...in 的顺序也遵循上面的规则。

尽管会遵循上面的规则,但是 for...in 还会遍历原型的属性。所以 for...in 的变量元素的规则是先按照我们上面讲的对象遍历规则去变量对象本身,接下来再按照此规则去遍历对象的原型,以此类推,直到遍历到顶部。

除了最后一个 for...in 的注意点之外,就没有其他的了,其实内容比较少。

到此这篇关于详解JS对象遍历的顺序问题的文章就介绍到这了,更多相关JS对象遍历顺序内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 利用jQuery的deferred对象实现异步按顺序加载JS文件

    前段时间看了阮一峰的jQuery的deferred对象详解一文,对jQuery中的deferred的用法了一些了解,今天看到园子里的一篇文章:关于重构JS前端框架的失败经验(顺便怀念那些死去的代码),于是把我之前写的一个利用jQuery的deferred异步按顺序加载JS文件方案分享出来,欢迎指正. 如果你现在对jQuery中的deferred还不了解,强烈建议你看一下阮一峰的jQuery的deferred对象详解一文. 加载JS文件的代码如下: 复制代码 代码如下: /* Loading Ja

  • 详解JS对象遍历的顺序问题

    可能有些同学听过在 JavaScript 中遍历对象顺序不固定的这一说法.事实上,这个说法不是特别准确. 对待遍历顺序,对象有一套自己既定的规则,在此规则下呢,对象的遍历顺序会受插入元素顺序的影响,但是不完全受插入元素先后顺序的影响.如果您有「必须按插入元素顺序遍历」的场景,可以考虑使用 Map. 遍历对象的方法有很多种,我们经常会使用的有 for...in ,除此之外,还有: Object.keys Object.entries Obejct.getOwnerProPertyNames Ref

  • 详解JS对象封装的常用方式

    JS是一门面向对象语言,其对象是用prototype属性来模拟的,下面,来看看如何封装JS对象. 常规封装 function Person (name,age,sex){ this.name = name; this.age = age; this.sex = sex; } Pserson.prototype = { constructor:Person, sayHello:function(){ console.log('hello'); } } 这种方式是比较常见的方式,比较直观,但是Per

  • 详解JS中遍历语法的比较

    for循环 JavaScript 提供多种遍历语法.最原始的写法就是for循环. let arr = [1,2,3,4,5]; for (var index = 0; index < arr.length; index++) { console.log(myArray[index]); // 1 2 3 4 5 } 缺点:这种写法比较麻烦 forEach 数组提供内置的forEach方法 let arr = [1,2,3,4,5]; arr.forEach((element,index) =>

  • js对象实例详解(JavaScript对象深度剖析,深度理解js对象)

    这算是酝酿很久的一篇文章了. JavaScript作为一个基于对象(没有类的概念)的语言,从入门到精通到放弃一直会被对象这个问题围绕. 平时发的文章基本都是开发中遇到的问题和对最佳解决方案的探讨,终于忍不住要写一篇基础概念类的文章了. 本文探讨以下问题,在座的朋友各取所需,欢迎批评指正: 1.创建对象 2.__proto__与prototype 3.继承与原型链 4.对象的深度克隆 5.一些Object的方法与需要注意的点 6.ES6新增特性 下面反复提到实例对象和原型对象,通过构造函数 new

  • 详解js创建对象的几种方式和对象方法

    这篇文章是看js红宝书第8章,记的关于对象的笔记(第二篇). 创建对象的几种模式: 工厂模式: 工厂是函数的意思.工厂模式核心是定义一个返回全新对象的函数. function getObj(name, age) { let obj = {} obj.name = name obj.age = age return obj } let person1 = getObj("cc", 31) 缺点:不知道新创建的对象是什么类型 构造函数模式: 通过一个构造函数,得到一个对象实例. 构造函数和

  • 详解js中的原型,原型对象,原型链

    理解原型 我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法.看如下例子: function Person(){ } Person.prototype.name = 'ccc' Person.prototype.age = 18 Person.prototype.sayName = function (){ console.log(this.name); } var person1 = ne

  • 详解JS中的对象字面量

    前言 在 ES6 之前,js中的对象字面量(也称为对象初始化器)是非常基础的.可以定义两种类型的属性: 键值对{name1: value1} 获取器{ get name(){..} }和 设置器{ set name(val){..}}的计算属性值 var myObject = { myString: 'value 1', get myNumber() { return this._myNumber; }, set myNumber(value) { this._myNumber = Number

  • 详解JS ES6变量的解构赋值

    1.什么是解构? ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构.它在语法上比ES5所提供的更加简洁.紧凑.清晰.它不仅能减少你的代码量,还能从根本上改变你的编码方式. 2.数组解构 以前,为变量赋值,我们只能直接指定值,比如 let a = 1; let b = 2; let c = 3; 现在可以用数组解构的方式来进行赋值 let [a, b, c] = [1, 2, 3]; console.log(a, b, c); // 1, 2, 3 这是数组解构最基本类型

  • 详解JS ES6编码规范

    1.块级作用域 1.1.let取代var ES6 提出了两个新的声明变量的命令: let 和const.其中,let可以完全取代var,因为两者语义相同,而且let没有副作用. var命令存在变量提升的特性,而let没有这个命令. 所谓变量提升,即指变量可以先使用,再声明,显然,这种编码规范非常不适合阅读. 1.2.全局常量和线程安全 在let和const之间,优先使用const. let应出现在单线程模块代码内,而const则非常适合多线程. // bad var a = 1, b = 2,

  • 详解JS异步加载的三种方式

    一:同步加载 我们平时使用的最多的一种方式. <script src="http://yourdomain.com/script.js"></script> <script src="http://yourdomain.com/script.js"></script> 同步模式,又称阻塞模式,会阻止浏览器的后续处理,停止后续的解析,只有当当前加载完成,才能进行下一步操作.所以默认同步执行才是安全的.但这样如果js中有输

随机推荐