javascript 检查对象是否类似于数组

xpszyzbs  于 2023-01-24  发布在  Java
关注(0)|答案(8)|浏览(123)

是否有方法检查对象是否“类似数组”,例如以下类型的对象:

  • 阵列(duh)
  • 类型化数组(Uint8Array等),当使用Array.isArray时,这些数组将返回false
  • 引数对象
  • 节点列表 *
  • 还有几个我一时想不起来的

我想你可以检查.length属性的存在,但是非数组类对象可以包含.length属性,我猜这些对象的共同点是数组访问器。

r7knjye2

r7knjye21#

有一种方法可以检查一个对象是否类似于数组,即使其中没有元素,在这里使用这个函数:

isArrayLike = function (_) {

    _[0] = 0; return [].slice.call(_).length >= Object.values(_).length;
}

这使用了我偶然发现的一个小技巧,它允许您确定对象是(1)数组、(2)类数组还是(3)类对象/类对象。
唯一的缺点是,对于添加了类似于对象的属性的类似数组的对象,如arguments,它不能正常工作

hzbexzde

hzbexzde2#

我认为没有什么比扩展本机对象更简单、更有表现力了:

Object.prototype.isArrayLike         = function(){ return false; };
Array.prototype.isArrayLike          = function(){ return true; };
NodeList.prototype.isArrayLike       = function(){ return true; };
HTMLCollection.prototype.isArrayLike = function(){ return true; };

这种方法可能会导致框架之间的冲突,但是,我建议您不要使用isArrayLike函数与其名称不符的框架。

1zmg4dgp

1zmg4dgp3#

正如我在这个主题的研究中发现的那样,你只有两个选择:
1.您可以只查看.length属性,并接受似乎具有适当.length属性的任何对象,这些对象不是您知道应该消除的任何其他对象(如函数)。
1.您可以检查特定的类似数组的对象(HTMLCollectionnodeList)并偏向它们。
下面是第一种方法的两个选项--一个不接受零长度,另一个接受零长度(这两个选项结合了gilly 3的建议以及我们在jQuery的类似函数中看到的内容):

// see if it looks and smells like an iterable object, but don't accept length === 0
function isArrayLike(item) {
    return (
        Array.isArray(item) || 
        (!!item &&
          typeof item === "object" &&
          item.hasOwnProperty("length") && 
          typeof item.length === "number" && 
          item.length > 0 && 
          (item.length - 1) in item
        )
    );
}

当然,这将为带有.length === 0的项报告false。如果您希望允许.length === 0,则可以使逻辑也包括这种情况。

// see if it looks and smells like an iterable object, and do accept length === 0
function isArrayLike(item) {
    return (
        Array.isArray(item) || 
        (!!item &&
          typeof item === "object" &&
          typeof (item.length) === "number" && 
          (item.length === 0 ||
             (item.length > 0 && 
             (item.length - 1) in item)
          )
        )
    );
}

一些测试用例:http://jsfiddle.net/jfriend00/3brjc/
2)在检查它不是一个实际的数组之后,可以编写代码来检查特定种类的类似数组的对象(例如nodeListHTMLCollection)。
例如,当我想确保包含nodeList和HTMLDollection类数组对象时,我使用了以下方法:

// assumes Array.isArray or a polyfill is available
function canAccessAsArray(item) {
    if (Array.isArray(item)) {
        return true;
    }
    // modern browser such as IE9 / firefox / chrome etc.
    var result = Object.prototype.toString.call(item);
    if (result === "[object HTMLCollection]" || result === "[object NodeList]") {
        return true;
    }
    //ie 6/7/8
    if (typeof item !== "object" || !item.hasOwnProperty("length") || item.length < 0) {
        return false;
    }
    // a false positive on an empty pseudo-array is OK because there won't be anything
    // to iterate so we allow anything with .length === 0 to pass the test
    if (item.length === 0) {
        return true;
    } else if (item[0] && item[0].nodeType) {
        return true;
    }
    return false;        
}
sy5wg1nm

sy5wg1nm4#

您可以检查对象是否为 iterable

function isIterable(o){
 return (o!=null && typeof(o[Symbol.iterator])==='function');
}

不过要小心,对于字符串***,**返回true。如果这是一个问题,请排除它们:

function isIterable(o){
 return (o!=null && typeof(o[Symbol.iterator])==='function' && typeof(o)!=='string');
}

然后使用迭代器访问元素,或者如果您想使用常规的 array[0] 方式,只需添加一个长度检查属性。

完整的isArrayLike函数:

function isArrayLike(a){
 return (
  a!=null &&
  typeof(a[Symbol.iterator])==='function' &&
  typeof(a.length)==='number' &&
  typeof(a)!=='string'
 );
}
soat7uwm

soat7uwm5#

好吧,这取决于你所说的类似于数组的东西,也许你可以用一个for循环来迭代它,就像这样:

for (var i=0, l=obj.length; i<l; ++i) {
  var item = obj[i];
}

所以测试很简单:

function isArrayLike(obj) {
  if (!obj) return false;
  var l = obj.length;
  if (typeof l != 'number' || l < 0) return false;
  if (Math.floor(l) != l) return false;
  // fast check
  if (l>0 && !((l-1) in obj)) return false;
  // more complete check (optional)
  for (var i=0; i<l; ++i) {
    if (!(i in obj)) return false;
  }
  return true;
}

当然,这不会捕捉到稀疏填充的数组,但是话又说回来,它们真的被用作数组吗?NodeList和类似的东西不会被稀疏填充。
"尽情享受吧"

rxztt3cl

rxztt3cl6#

在JavaScript中,类似数组的对象是包含length属性的对象,该属性具有非负整数值。长度大于零的类似数组的对象还包含额外的n属性,该属性从[0]开始,一直到[n-1],其中n = length的值。在此定义下,字符串也类似数组,并在以下函数中返回true。
最后我写了三个函数,因为不检查所有索引元素的存在可以保存时间,这些函数可以在旧的浏览器中工作。
这个函数只检查最后一个元素的存在,这使得它的时间复杂度为O(1)。

function isArrayLike(o) {
    var n;
    return ({}).toString.call(o).split(' ')[1] in {"String]":1,"Array]":1} || o!=null
        // Confirm that o.length is a non-negative integer
        && (n=o.length)===Math.abs(n|0)
        // And when non-zero confirm that o.length-1 is a property in o
        && (!n || n-1 in o);
}

这个函数检查所有索引元素的存在性,这使得它的时间复杂度为O(n)。

function isArrayLikeStrict(o) {
    var n = 0;
    if (({}).toString.call(o).split(' ')[1] in {"String]":1,"Array]":1} || o!=null
        // Confirm that o.length is a non-negative integer
        && (n=o.length)===Math.abs(n|0)
    ) {
        // Confirm existence of all indexed elements
        while (n--) if(!(n in o)) return false;
        return true;
    }
    return false;
}

这个函数检查log 2(n)+1个索引元素的存在性,这使得它的时间复杂度为O(log n),这可能是你最好的选择,它比第一个更严格,但避免了第二个的O(n)复杂度。

function isArrayLikeLessStrict(o) {
    var n = 0;
    if (({}).toString.call(o).split(' ')[1] in {"String]":1,"Array]":1} || o!=null
        // Confirm that o.length is a non-negative integer
        && (n=o.length)===Math.abs(n|0)
    ) {
        // Confirm existence of all indexed elements
        while (n) {
            console.log(n);
            if(!(n-1 in o)) return false;
            n = n/2|0;
        }
        return true;
    }
    return false;
}
sigwle7e

sigwle7e7#

从技术上讲,(几乎)根据标准(ECMAScript 2015语言规范§7.3.17,CreateListFromArrayLike (obj [, elementTypes] )),每个对象都是“类似数组的”(因为undefined的类型强制):

7.3.17从类数组创建列表(对象[,元素类型])

抽象操作CreateListFromArrayLike用于创建List值,其元素由类似数组的对象 obj 的索引属性提供。可选参数 elementTypes 是一个List,其中包含ECMAScript语言类型的名称,这些名称允许用于创建的List的元素值。此抽象操作执行以下步骤:
1.突然返回(* 对象 )。
1.如果未传递 elementTypes,则让 elementTypes 为(未定义、空、布尔、字符串、符号、数字、对象)。
1.如果类型(obj)不是Object,则引发TypeError异常。
1.设 len 为目标长度(Get(obj"length"))。
1.突然返回(len)。
1.令 list 为空List。
1.令 index 为0。
1.当 indexlen 时重复
1.令 * 索引名称 * 为目标字符串(
索引 )。
1.令 next 为Get(
对象 索引名称 )。
1.突然返回(
下一次 *)。
1.如果类型(next)不是 elementTypes 的元素,则引发TypeError异常。
1.追加 next 作为 list 的最后一个元素。
1.将 * 索引 * 设置为 * 索引 * + 1。
1.返回 * 列表 *。
通过https://www.browserling.com/tools/html-to-markdown生成

new9mtju

new9mtju8#

Array是具有以下属性的值:
1.其类型为object
1.它具有等于或大于0的 * length * 属性。
1.所有基于值的键都是数字,并且大于或等于0。

  • 例外情况:
  • 长度
  • 值是一个函数。
function isArrayLike(value) {
  if (typeof value === "object" && !!value) {
    const isArray = Array.isArray(value);
    const allNumericKeys = Object.keys(value).every((k) =>
      (!isNaN(k) && +k >= 0) ||
      k === "length" ||
      typeof value[k] === "function"
    )
    const hasLength = value.length > 0
    return isArray || (allNumericKeys && hasLength)
  }
  return false
}

console.log('Array: ', isArrayLike([]))
console.log('Array Like: ', isArrayLike({1: 'a', 2: 'b', length: 3}))
console.log('Array Like with function: ', isArrayLike({1: 'a', 2: 'b', length: 3, foo: () => {} }))
console.log('Array Like with negative keys: ', isArrayLike({ "-1": 'a', "-2": 'b', length: 1}))
console.log('Array Like without length:', isArrayLike({1: 'a', 2: 'b' }))
console.log('Node List: ', isArrayLike(document.querySelectorAll('p')))
console.log('null: ', isArrayLike(null))
console.log('String: ', isArrayLike('Test'))
console.log('Number: ', isArrayLike(123))
<div>
  <p></p>
  <p></p>
  <p></p>
  <p></p>
</div>

相关问题