我正在寻找一种有效的方法来测试JavaScript对象是否是某种有序对象。希望有人知道一个不涉及编写一些巨大的类型评估函数的技巧。幸运的是,我没有处理IE< 9所以大多数新方法都没问题。
根本问题在于:我需要弄清楚是否在对象上运行 for 循环或 for ... in 循环。但是,我并不总是知道对象是否是对象文字,数组,jQuery对象等。
以下是我遇到的一些障碍:
我显然不仅可以使用typeof
,因为数组和对象都返回object
。
我显然不仅可以使用Object.prototype.toString.call(collection)
,因为虽然数组返回[object Array]
,但自定义有序集合(如jQuery对象)仍会返回[object Object]
,我希望此测试的全部原因是确定我是否需要 for 循环或 for ... in 循环。在jQuery对象上使用 for ... in 循环包含实际上不属于集合的属性并使事情变得混乱。
我确实提出了一个看起来像这样的想法:
function isOrdered(item) {
// Begin by storing a possible length property
// and defaulting to false for whether the item
// is ordered.
var len = item.length, isOrdered = false;
// Functions are an easy test.
if (typeof item === 'function') {
return false;
}
// The Arguments object is the only object I know of
// with a native length property that can be deleted
// so we account for that specifically too.
if (Object.prototype.toString.call(item) === '[object Arguments]') {
return true;
}
// Attempt to delete the item's length property.
// If the item is ordered by nature, we won't get
// an error but we also won't be able to delete
// this property.
delete item.length;
// So if the length property still exists as a
// number, the item must be an ordered collection.
if (typeof item.length === 'number') {
isOrdered = true;
}
// If we originally stored a custom length property,
// put it back.
if (len !== undefined) {
item.length = len;
}
// Return the result.
return isOrdered;
}
到目前为止,这项技术已经通过了我的所有测试,但我担心可能会因删除自定义对象上的属性而导致性能下降,而且我并不是100%自信我没有错过任何东西。有没有人有更好的想法?
答案 0 :(得分:0)
如果你的技巧在Firefox,Chrome和最近的IE中工作,我认为它可能没问题......虽然有点不稳定。
删除通过并删除对象的键和值,而不是仅清除引用。如果它不能删除属性,它实际上将返回false,因此您可以进一步收紧逻辑。
属性删除有一些性能开销,它对当前在内存 [see this article]中的常规对象做了一些奇怪的事情,但是根据浏览器可以忽略不计。这真的取决于你一次要做多少个收藏。对于单个数组/对象/神秘,我认为它会没事的。但是对于数千个数组的嵌套集合,您可能希望研究其他一些替代方案。
如果您主要关注性能,那么这是一个JS性能: http://jsperf.com/delete-vs-undefined-vs-null/3
<强>更新强> 或者,如果您关注Jquery集合与数组,则可以执行以下操作:
if (foo.length != undefined) {
if (Array.isArray(foo)) {
// regular array. Could also do
// foo instanceof Array
}
else if(foo.constructor.toString().match(/jQuery/g)) {
// jquery collection
}
} else {
// not an ordered collection
}
答案 1 :(得分:0)
您可以这样做:
function isOrdered(item) {
var len = item.constructor.prototype.length;
return len === +len;
}
如果'item'是一个js对象,这将评估为false,但如果它是一个数组或jQuery集合,则返回true。
请注意,除数组之外的其他内容可以具有数字长度属性(尝试检查函数的长度)。如果你可能传递整数或函数之类的东西,你需要做一些更复杂的事情,但是这应该能够捕获你在发布的代码中捕获的所有情况。此外,我认为删除length属性可能会产生不可预测的结果,所以我会避免使用这种方法来支持那些不会改变你正在测试的对象的东西。