在console.log中显示对象属性的原始顺序。

21

我需要进行一些调试,以查看一个JavaScript对象属性的原始顺序,但是(至少在Chrome开发工具中),console.log()会显示按字母顺序排序的对象。

例如:

var obj = {
    z: 1,
    t: 2,
    y: 3,
    a: 4,
    n: 5,
    k: 6
}

console.log(obj) 显示如下内容:

Object {z: 1, t: 2, y: 3, a: 4, n: 5}
a:4
k:6
n:5
t:2
y:3
z:1

//expected (needed ) original order
z: 1
t: 2
y: 3
a: 4
n: 5
k: 6

4
你真的相信 JavaScript 对象属性有顺序吗? - Gerardo Furtado
4
对象是一个无序的集合,不能保证你得到任何特定的顺序。听起来你调试的方法可能需要微调。 - user1106925
1
你必须使用数组,因为对象不能保证每次迭代时属性的顺序都相同。而数组确保了顺序。 - Swimburger
3
这些注释太过时了... - Klesun
1
是的,这些评论已经过时了 - 但是说出原因还是有用的! 这里有一个好的答案,可以确认它们确实有顺序 https://dev59.com/4m035IYBdhLWcg3wW-v0#31102605。然而要小心,因为Chrome devtools在检查器中会对属性进行排序,这可能不是它们被添加/迭代的顺序。这会极大地增加混淆,直到你意识到发生了什么!(或者如果你忘记了) - Simon_Weaver
6个回答

30

console.log 确实会对属性进行排序,但在某些情况下,您可以使用 JSON.stringify 保留顺序,例如:

console.log确实会对属性进行排序,但在某些情况下,您可以使用JSON.stringify保留顺序,例如:

console.log(JSON.stringify(obj, null /*replacer function */, 4 /* space */))

注意:与普遍的信念相反,JS对象按照OwnPropertyKeys规范维护枚举顺序(首先是整数,然后是按插入顺序的其他属性)。


1
很遗憾,您不能使用这种方法折叠键。 - Klesun

9
对象会保留它们(非数字)键的插入顺序,但是只有使用特定方法迭代时才能“保证”按照该顺序迭代。根据规范,Object.keys 及其变体、JSON.stringifyfor..in 循环都无法按特定顺序迭代。这些方法都调用EnumerateObjectProperties,其中明确说明:

枚举属性的机制和顺序未指定

虽然大多数情况下,这些方法在可预测的顺序中迭代,但是这种行为绝不能被规格保证。
但是,Object.getOwnPropertyNames (以及 Reflect.ownKeysObject.getOwnPropertySymbols) 是保证按特定顺序迭代的:升序数字键,然后是插入顺序的其他键,根据 [[OwnPropertyKeys]]
因此,记录按插入顺序排列的(非数字)属性的规范保证方法将涉及使用上述方法之一,而不是使用Object.keys或其变体:

var obj = {
  z: 1,
  t: 2,
  y: 3,
  a: 4,
  n: 5,
  k: 6
};

const str = '{\n' +
  Object.getOwnPropertyNames(obj).map(key => `  ${key}: ${obj[key]}`).join('\n')
  + '\n}';
console.log(str);

针对嵌套对象,你需要使用递归函数来处理:

var obj = {
  z: 1,
  t: 2,
  y: 3,
  a: 4,
  nested: {
    foo: 9,
    bar: 99,
    baz: 35
  },
  n: 5,
  k: 6
};

const objToArrOfLines = (obj, lines=[], leftPadding=0, keyForThisObj) => {
  lines.push(`${' '.repeat(leftPadding)}${keyForThisObj ? keyForThisObj + ': ' : ''}{`);
  Object.getOwnPropertyNames(obj).forEach((key) => {
    const val = obj[key];
    if (typeof val === 'object') {
      objToArrOfLines(val, lines, leftPadding + 2, key);
    } else {
      lines.push(`${' '.repeat(leftPadding + 2)}${key}: ${val}`);
    }
  });
  lines.push(`${' '.repeat(leftPadding)}}`);
  return lines;
};
const objToStr = (obj) => {
  console.log(objToArrOfLines(obj).join('\n'));
};

objToStr(obj);


很好的评论!同时,Map对象也保证了键插入的顺序。 - Andrey Smolko

2
console.table(yourObj)的输出顺序与预期相同,但以表格形式呈现。
注意:在现代浏览器中,数字键(即使用引号括起来)始终按从小到大排序,但一旦键中出现非数字字符,它将忽略该键的排序。有一些hackish的解决方法可以利用这个事实,比如在所有键前加上_或其他非数字字符,以防止对象被自动排序。更多信息请参见https://www.stefanjudis.com/today-i-learned/property-order-is-predictable-in-javascript-objects-since-es2015/

1
谢谢,这个信息很有用。这是一个有效的选项。 - MTK

2

仅供澄清,Console.log会排序,但也不会......

这是断点列表,正确的顺序(按创建顺序)为defaultmobileMmobileLtabletdesktopM,这就是第一行显示的内容。

enter image description here

但是当你展开它时,它们是按字母顺序排列的。如果你悬停在某个东西上并查看弹出窗口,也是一样的。
重点是有时它会排序,有时又不会。实际上应该有一个选项,但似乎没有 - 如果你通常不关心属性顺序,这种混合行为可能会让你陷入困境。

2
另一个简单的解决方案是:

最初的回答

console.log(Object.entries(obj).map(k=>({[k[0]]:k[1]})))

1
如果您需要记录一个非常大的对象,并且希望能够折叠键,则另一个选项是将其转换为键值对数组。
let keepKeyOrder = function(obj) {
    if (typeof obj === 'object' && !Array.isArray(obj)) {
        let transformKey = (k) => [k, keepKeyOrder(obj[k])];
        return Object.keys(obj).map(transformKey);
    } else {
        return obj;
    }
};

console.log(keepKeyOrder({a:3,c:4,b:{b3:123,b2:234,b1:345}}));

输出:

enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接