关于js中for...in循环对象时,输出key值顺序混乱问题

解决循环复杂对象,key值顺序混乱

问题描述

当循环纯数字索引对象时,循环key值是正确的

在这里插入图片描述

当对象变为复杂对象时,输出的key就变得复杂

在这里插入图片描述

解决方案

//循环中使用
for(let item in this.objectOrder(data)){
    
    
	this.objArr.push(item)
}
 
//方法
objectOrder(obj) {
    
    //排序的函数
	var newkey = Object.keys(obj).sort(); //先用Object内置类的keys方法获取要排序对象的属性名,再利用Array原型上的sort方法对获取的属性名进行排序,newkey是一个数组
	var newObj = {
    
    };//创建一个新的对象,用于存放排好序的键值对
	for (var i = 0; i < newkey.length; i++) {
    
    //遍历newkey数组
	    newObj[newkey[i]] = obj[newkey[i]];//向新创建的对象中按照排好的顺序依次增加键值对
	}
	return newObj;//返回排好序的新对象
}

在这里插入图片描述

循环对象时,输出key值顺序混乱原因

ES6之前,循环对象常见做法是使用:for…in。但是for…in循环的问题在于它会遍历原型链中的属性,所以需要使用hasOwnProperty执行检查属性是否属于该对象。

ES6之后,我们对于对象的循环有了更好的办法:

  • Object.keys(创建一个包含对象所有属性的数组),
const fruits ={
    
    
    appple:22,
    pear:34,
    orange:88
}
var keys = Object.keys(fruits);
console.log(keys);  //["appple", "pear", "orange"]
  • Object.values(创建一个数组,其中包含对象中每个属性的值),
const fruits ={
    
    
    appple:22,
    pear:34,
    orange:88
}

var values =Object.values(fruits);
console.log(values); //[22, 34, 88]
  • Object.entries(创建了一个二维数组,每个内部数组都有2个元素,第一个元素是属性名,第二个属性值)
const fruits ={
    
    
    appple:22,
    pear:34,
    orange:88
}
var entries = Object.entries(fruits);
console.log(entries);
// [['appple',22],['pear',34],['orange',88]]
const fruits ={
    
    
    appple:22,
    pear:34,
    orange:88
}
for (const [fruit,num] of entries) {
    
    
    console.log(`we have ${
      
      num} ${
      
      fruit}`);  //we have 22 appple ...
}

Object对应的方法也存在相同的问题,可以使用类似的方法进行修改

下面主要说的是 for…in

循环对象时,顺序为什么会乱?

1.这本身就是一个ECMA的一个规范,数字按升序输出,字符串按创建顺序输出,并且数字优先级高于字符串
2.JS本身是不能被计算机识别,需要通过V8转化为字节码
3.那么针对ECMA的一个规范,v8对这个规范做的优化策略
4.排序属性 elements,用来存储数字。 常规属性 properties 用来存储字符串。为了优化,引入对象内属性
5.对象属性多后,会采用慢属性来存储数据,快属性就是采用线性数据结构,慢属性就是采用非线性结构,比如字典来存储数据。

1. 先遍历出整数属性(integer properties,按照升序),然后其他属性按照创建时候的顺序遍历出来。

  • 整数属性
String(Math.trunc(Number(prop)) === prop

当上面的判断结果为 true,prop 就是整数属性,否则不是。

例:

"49" 是整数属性,因为 String(Math.trunc(Number('49')) 的结果还是 "49"。
"+49" 不是整数属性,因为 String(Math.trunc(Number('+49')) 的结果是 "49",不是 "+49"。
"1.2" 不是整数属性,因为 String(Math.trunc(Number('1.2')) 的结果是 "1",不是 "1.2"。

「数字属性应该按照索引值⼤⼩升序排列,字符串属性根据创建时的顺序升序排列。并且数字属性优先于字符串」

2. 首先JS代码本身是不会直接被计算机执行,计算机只能接收二进制的汇编代码,所以,中间需要一层转化,而这个转化,在chrom就是v8引擎

在v8 里是怎么样存储和读取对象属性的呢,

1. 在v8里,将对象里的属性,分为两大类。数字类型,叫排序属性,在v8里叫elements。字符串类型,叫常规属性,在v8里叫properties。
在v8里,为了有效的存储和访问这对象属性,分别使用两个线性结构来保存这两个属性。

2. 在elements对象中,会按照顺序存放排序属性,properties属性则指向了properties对 象,在properties对象中,会按照创建时的顺序保存了常规属性。

3. 但是这样也存在一个问题,在查找排序属性时,直接通过索引即可。但是对象,需要找到properties,然后找到propteries里的属性,这样无疑多了一层操作,所以引入了一个新名词 对象内属性( in- object properties)

4. 但是常规属性也有个数限制,超过是个,默认是10个,就要开辟新的空间来保存常规属性

针对数量少的对象属性,采用以上策略完全没有问题,但是对象数量多了以后,会采用排序非线性字典结构来存储

  • 线性结构:是一个有序数据元素的集合。常见线性结构, 线性表,栈,队列,双队列,串(一维数组)
  • 非线性结构: 其逻辑特征是一个结点元素可能有多个直接前驱和多个直接后继。

那这个时候,通过线性结构来存储数据,查找肯定的快的,但是如果涉及到大量的修改数据,那么动一发而牵全身,是非常耗性能的,所以数据多了,v8采用了慢排序

  • 快排序: 采用线性结构
  • 慢排序:采用非线性结构 ,比如字典

3. 同时与浏览器有关系,Chrome跟IE是不一样的,所以给出以下结论:

Chrome Opera 的 JavaScript 解析引擎遵循的是新版 ECMA-262 第五版规范。因此,使用 for-in 语句遍历对象属性时遍历书序并非属性构建顺序。
而 IE6 IE7 IE8 Firefox Safari 的 JavaScript 解析引擎遵循的是较老的 ECMA-262 第三版规范,属性遍历顺序由属性构建的顺序决定。

Chrome Opera 中使用 for-in 语句遍历对象属性时会遵循一个规律:
它们会先提取所有 key 的 parseFloat 值为非负整数的属性,然后根据数字顺序对属性排序首先遍历出来,然后按照对象定义的顺序遍历余下的所有属性。

https://blog.csdn.net/wk15038187622/article/details/104062244

猜你喜欢

转载自blog.csdn.net/weixin_52755319/article/details/129873982