在 console.log 中显示对象属性的原始顺序
Show original order of object properties in console.log
我需要进行一些调试以查看一个 JavaScript 对象属性的原始顺序,但是(至少在 chrome devtools 中)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
console.log
确实对属性进行排序,在某些情况下,您可以使用 JSON.stringify
来保留顺序,例如
console.log(JSON.stringify(obj, null /*replacer function */, 4 /* space */))
注意:与普遍的看法相反,js 对象按照 OwnPropertyKeys specification 维护枚举顺序(首先是整数,然后是插入顺序中的其他属性)
如果您需要记录一个非常大的对象,以便能够折叠键,另一种选择是将其转换为键值对数组。
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}}));
输出:
另一个简单的解决方案是:
console.log(Object.entries(obj).map(k=>({[k[0]]:k[1]})))
对象确实保留其(非数字)键插入的顺序,但它们仅保证使用某些方法按该顺序迭代。根据规范,Object.keys
及其变体 JSON.stringify
和 for..in
循环都以未指定的顺序迭代。这些方法都调用了EnumerateObjectProperties,其中明确指出:
The mechanics and order of enumerating the properties is not specified
虽然对于这些方法,环境通常以可预测的顺序进行迭代,但规范无法保证这种行为。
但是,Object.getOwnPropertyNames
(and Reflect.ownKeys
, and Object.getOwnPropertySymbols
) are guaranteed to iterate in a particular order: ascending numeric keys, followed by other keys in insertion order, per [[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);
澄清一下 Console.log
确实排序了,但也没有....
这是一个断点列表,正确的顺序(创建时)是 default
、mobileM
、mobileL
、tablet
、desktopM
这就是第一行显示的内容。
但是当您展开它时,它们是按字母顺序排列的。如果将鼠标悬停在某处并查看弹出窗口,则相同。
重点是前一分钟它对它们进行了排序,下一分钟就没有。确实应该是一个选项,但似乎不是 - 如果您通常不关心 属性 顺序,这种混合行为真的会让您感到困惑。
我需要进行一些调试以查看一个 JavaScript 对象属性的原始顺序,但是(至少在 chrome devtools 中)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
console.log
确实对属性进行排序,在某些情况下,您可以使用 JSON.stringify
来保留顺序,例如
console.log(JSON.stringify(obj, null /*replacer function */, 4 /* space */))
注意:与普遍的看法相反,js 对象按照 OwnPropertyKeys specification 维护枚举顺序(首先是整数,然后是插入顺序中的其他属性)
如果您需要记录一个非常大的对象,以便能够折叠键,另一种选择是将其转换为键值对数组。
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}}));
输出:
另一个简单的解决方案是:
console.log(Object.entries(obj).map(k=>({[k[0]]:k[1]})))
对象确实保留其(非数字)键插入的顺序,但它们仅保证使用某些方法按该顺序迭代。根据规范,Object.keys
及其变体 JSON.stringify
和 for..in
循环都以未指定的顺序迭代。这些方法都调用了EnumerateObjectProperties,其中明确指出:
The mechanics and order of enumerating the properties is not specified
虽然对于这些方法,环境通常以可预测的顺序进行迭代,但规范无法保证这种行为。
但是,Object.getOwnPropertyNames
(and Reflect.ownKeys
, and Object.getOwnPropertySymbols
) are guaranteed to iterate in a particular order: ascending numeric keys, followed by other keys in insertion order, per [[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);
澄清一下 Console.log
确实排序了,但也没有....
这是一个断点列表,正确的顺序(创建时)是 default
、mobileM
、mobileL
、tablet
、desktopM
这就是第一行显示的内容。
但是当您展开它时,它们是按字母顺序排列的。如果将鼠标悬停在某处并查看弹出窗口,则相同。
重点是前一分钟它对它们进行了排序,下一分钟就没有。确实应该是一个选项,但似乎不是 - 如果您通常不关心 属性 顺序,这种混合行为真的会让您感到困惑。