博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Object.keys(..)对象属性的顺序?
阅读量:7187 次
发布时间:2019-06-29

本文共 4588 字,大约阅读时间需要 15 分钟。

在开发中遇到了这样一个麻烦,需要比较两个庞大的object数据结构对应属性的值是否发生了变化,并得标记这个属性,比如:

const initialData = {
name:"rockets",team:["Harden","Paul"],location:{
country:"USA",city:"Houston"},goal:[{
champion:true}]};const changedData = {
name:"rockets",team:["Harden","Paul","House"],location:{
country:"USA",city:"Houston"},goal:[{
champion:true}]};calcEqual(initialData,changedData);//得到类似{name:true,team:false,location:true,goal:true}结果//true表示值一致,false表示值发生改变复制代码

首先确定的是得遍历objectkeys

遇到的问题是如何判断前后两个值是否相等,特殊的是teamlocationgoal这样引用值的比较(包括引用值还内嵌引用值)。因为内嵌之后情况比较复杂,目标指向了先字符串化,再比较:

  1. xx.toString()?像team这样没有嵌套的数组值可以比较,但是var a = {};a.toString === "[object Object]",涉及对象的比较会失败。//不能用
  2. JSON.stringify()?这里有两个问题://不能用
    • 涉及JSON非法值,以及部分特殊值,如JSON.stringify(NaN)==="null",结果是得不到保证的
    • JSON.stringify()处理object值,不保证object的属性顺序()
  3. 决定手动将引用值转化为字符串 //待定
function preTreatment(value){  //基础类型直接返回  if(typeof value!=="object") return String(value);  if(Object.is(value,null)) return String(value);    //引用类型  //数组  if(Object.prototype.toString.call(value)==="[object Array]"){    return value.map(v=>preTreatment(v)).join(",");  }    //对象  if(Object.prototype.toString.call(value)==="[object Object]"){    const keys = Object.keys(value);        return keys.map(k=>{      if(typeof value[k]!=="object") return `${k}:${preTreatment(value[k])}`;      return preTreatment(value[k]);    }).join(",")  }}复制代码

这里拆解了嵌套的情况,但是存在的风险点是Object.keys()不保证属性的顺序性。从这句话开始,出发在Object.keys “歧途”上越走越远。

Object.keys不保证对象属性的顺序?

本着怀疑这句话的态度去查了:

Object.keys() returns an array whose elements are strings corresponding to the enumerable properties found directly upon object. The ordering of the properties is the same as that given by looping over the properties of the object manually.

没有直接说明,只是说和手动遍历相同,那查一下 :

Array indexes are just enumerable properties with integer names and are otherwise identical to general object properties. There is no guarantee that for...in will return the indexes in any particular order...Because the order of iteration is implementation-dependent.

因为迭代的顺序是依赖于浏览器实现的,结论是不保证。

保证对象属性的顺序

如果Object.keys()不能保证对象属性的顺序,那么JavaScript有没有其他方法是可以保证对象属性的顺序。

ownPropertyKeys:规定对象属性遍历顺序

找到了一篇简洁风格的文章:,它提到了JavaScript内部的ownPropertyKeys方法,它定义了对象属性遍历的顺序:

  1. integer-like keys in ascending order
  2. normal keys in insertion order
  3. Symbols in insertion order
  4. if mixed, order: interger-like, normal keys, Symbols

interget-like keys,除了0,1这样的数字,也包括"2",这种键类型按照从小到大顺序返回;normal keys也就是string类型,和Symbol一样都是按照插入顺序返回。如果包含多种类型,按照 interger-like keys、normal keys、Symbols顺序从前往后。(控制台输出的顺序貌似也是这样的...)

Object.getOwnPropertyNames()

基于内部ownPropertyKeys方法实现的方法有Object.getOwnPropertyNamesReflect.ownKeys,这两种方法保证对象属性的顺序。

Reflect.ownKeys()返回的结果等价于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)),包括直接挂在目标对象上的可枚举、不可枚举、Symbols的属性组成的数组。但是这里不选择这个方法,主要考虑兼容性(ES6提出,ie不支持):

Object.getOwnPropertyNames()返回直接挂在目标对象上的可枚举、不可枚举属性组成的,在ES5提出,兼容性更好,支持IE9+:

到这里修改下最开始贴出的preTreatment方法:

function preTreatment(value){  //基础类型直接返回  if(typeof value!=="object") return String(value);  if(Object.is(value,null)) return String(value);    //引用类型  //数组  if(Object.prototype.toString.call(value)==="[object Array]"){    return value.map(v=>preTreatment(v)).join(",");  }    //对象  if(Object.prototype.toString.call(value)==="[object Object]"){    const keys = Object.getOwnPropertyNames(value);		//替换Object.keys        return keys.map(k=>{      if(typeof value[k]!=="object") return `${k}:${preTreatment(value[k])}`;      return preTreatment(value[k]);    }).join(",")  }}复制代码

手动为Object.keys()添加顺序

突然想到,虽然Object.keys()本身返回的对象属性的数组无法保证顺序,但是可以手动为返回的结果排序,再加上项目中遇到的属性名都是normal key即字符串类型,只需替换为Object.keys(value).sort()

然而 lodash...

然而问了一下同事之后,被告知lodash有一个isequal方法:

Performs a deep comparison between two values to determine if they are equivalent.

Note: This method supports comparing arrays, array buffers, booleans, date objects, error objects, maps, numbers, Object objects, regexes, sets, strings, symbols, and typed arrays. Object objects are compared by their own, not inherited, enumerable properties. Functions and DOM nodes are compared by strict equality, i.e. ===.

嗯...符合这里的场景。

然后好奇心驱使大致看了一下lodash的isequal源码,在深度比较object时,是取相同的key对应的value值作比较,也就没有字符串化的过程,也没有涉及对象属性的顺序问题...(手动这样实现有点绕,但是可以直接用,而且安全)

嗯...好吧,最后还是妥协使用了lodash的isequal方法。

小结

对象属性的遍历,在Object.keys()for..in循环时候是一个模糊地带,不保证对象属性的顺序;Object.ownPropertyNames基于内建方法ownPropertyKeys,有一个保证的对象属性顺序,而且兼容性很好,支持IE9+;

另外在日常开发中,需要方法解决难解决的问题时,优先查阅一下像lodash这样的库有没有实现的方法,提高平时开发效率,而在有空时候再去深入研究

兼容性查询工具

  • http://kangax.github.io/compat-table/es5/
  • https://caniuse.com/

参考链接

转载于:https://juejin.im/post/5c88ce0e5188257f882f0ef8

你可能感兴趣的文章
project.pbxproj 文件的组织及说明
查看>>
Android 网络请求方面的资料
查看>>
ajax跨域的解决办法
查看>>
ZooKeeper管理指南
查看>>
jqGrid随浏览器缩放自适应宽度
查看>>
JavaScript函数补完:splice()数组操作
查看>>
Souce Control Management-EGit
查看>>
长连接的心跳及重连设计
查看>>
ORA-00020: maximum number of processes (1000) 错误处理
查看>>
cas单点登录集群如何优雅的退出
查看>>
[置顶] spring2.5 + struts2 + ibatis2.3.4 框架整合开发
查看>>
第一次作业
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
安装配置管理 之 安装和配置 Java J2SE Development Kit(JDK)
查看>>
ORACLE常用函数实例
查看>>
php 调用webservers 错误,请高手帮助
查看>>
软件包管理 之 Freshrpms.net 资源介绍,及apt 和yum 的应用
查看>>
我的友情链接
查看>>