繁体   English   中英

检查 object 是否类似于数组

[英]Checking if an object is array-like

有没有办法检查 object 是否“类似数组”,例如这些类型的对象:

  • Arrays(呃)
  • 输入 Arrays(Uint8Array 等),使用Array.isArray时这些将返回 false
  • arguments object
  • 节点列表*
  • 还有其他一些我暂时想不起来的

我想您可以检查是否存在.length属性,但非类数组对象可以包含.length属性。 我想这些都有一个共同点是数组访问器。

正如我在对这个主题的研究中发现的那样,你只有几个选择:

  1. 您只能查看.length属性并接受似乎具有适当.length属性的任何对象,而该属性不是您知道应该消除的任何其他内容(如函数)。

  2. 您可以检查特定的类似数组的对象( HTMLCollectionnodeList )并偏向于它们。

这是第一种方法的两个选项 - 一个不接受零长度,一个接受(这些结合了gilly3的建议和我们在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 和 HTMLCollection 类数组对象时,我使用了以下方法:

// 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;        
}

好吧,这取决于您所说的类似数组的含义。 可能你可以用这样的 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;
}

当然,这不会捕获人口稀少的数组,但话说回来,它们真的被用作数组吗? NodeLists 和类似的东西不会被稀疏地填充。

享受!

您可以检查对象是否可迭代

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

不过小心,对于 strings 返回 true 如果这是一个问题,请排除它们:

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

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


完整的 isArrayLike 函数:

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

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

7.3.17 CreateListFromArrayLike (obj [, elementTypes] )

抽象操作 CreateListFromArrayLike 用于创建一个 List 值,其元素由类数组对象obj的索引属性提供。 可选参数elementTypes是一个List,其中包含允许用于创建的List 的元素值的 ECMAScript 语言类型的名称。 此抽象操作执行以下步骤:

  1. ReturnIfAbrupt ( obj )。
  2. 如果elementTypes不获通过,让elementTypes是(未定义,空,布尔,字符串,符号,数字,对象)。
  3. 如果Type ( obj ) 不是 Object,则抛出TypeError异常。
  4. lenToLength ( Get ( obj , "length" ))。
  5. ReturnIfAbrupt ( len )。
  6. list为空List
  7. 索引为 0。
  8. index < len 时重复
    1. indexNameToString ( index )。
    2. 接下来Get ( obj , indexName )。
    3. ReturnIfAbrupt下一个)。
    4. 如果Type ( next ) 不是elementTypes的元素,则抛出TypeError异常。
    5. 追加next作为list的最后一个元素。
    6. 索引设置为索引+ 1。
  9. 返回列表

通过https://www.browserling.com/tools/html-to-markdown生成

我写这个函数是因为我需要一些可以在旧浏览器中工作的东西。

function isArrayLike(object) {
    var length
    if (object==null) {// same as object==undefined
        return false
    }

    try {// This throws an error in old browsers
        return typeof object[Symbol.iterator]=="function"
    }
    catch (e) {
        // Check that object.length is a non-negative integer
        return (length=object.length)===Math.abs(length|0)
            // And when non-zero check that length-1 is in object
            && (typeof object=="string" || !length || length-1 in object)
    }
}

数组是具有以下属性的值:

  1. 其类型 object
  2. 它具有等于或大于 0 的长度属性。
  3. 所有基于值的键都是数字,并且大于或等于 0。
  • 例外情况:
  • 长度
  • 值为 function。

 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>

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

isArrayLike = function (_) {

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

这使用了我偶然发现的一个小技巧,它允许您确定一个对象是 (1) 数组,(2) 类数组,或 (3) 对象/类对象。

唯一的缺点是它对于添加了类对象属性(例如arguments类数组不能正常工作

我说没有什么比扩展原生对象的简单性和表现力更胜一筹了:

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函数与名称所暗示的功能不符的框架保持距离。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM