【问题标题】:Creating array-like objects in JavaScript在 JavaScript 中创建类似数组的对象
【发布时间】:2012-08-09 15:17:27
【问题描述】:

在 JavaScript 中,有些对象伪装成数组(或“类数组”)。此类对象有argumentsNodeLists(从getElementsByClassName等返回)和jQuery对象。

console.logged 时,它们显示为数组,但不是。我知道,为了像数组一样,对象必须具有length 属性。

所以我做了一个这样的“对象”:

function foo(){
    this.length = 1;
    this[0] = "bar";
}

var test = new foo;

当我console log(test) 时,我(如预期)得到一个foo 对象。我可以使用

将其“转换”为数组
Array.prototype.slice.call(test)

但是,我不想转换它,我希望它像数组一样。如何制作一个类似数组的对象,以便当它是console.logged 时,它会显示为一个数组?

我尝试设置foo.prototype = Array.prototype,但console.log(new foo) 仍然显示foo 对象,而不是数组。

【问题讨论】:

  • “我如何制作一个类似数组的对象,以便当它被 console.logged 时,它显示为一个数组?” 仅仅因为控制台没有显示它使用 Array 字面量语法或其他一些让您想到 Array 的语法,并不意味着它不是 "array-like"。控制台如何显示您的数据与该数据是什么无关。
  • @amnotiam,虽然这是真的,但在这种情况下,我发现协助控制台帮助我很有用。当我记录一个“类似数组”的对象时,我并不真正关心对象,而是关心它的内容。
  • @amnotiam:我只是有点好奇 jQuery 对象和 arguments 是如何工作的。我想知道为什么它们被记录为数组。 :-P
  • @zzzzBov:我不同意这一点。我只是说拥有“类似数组的对象”与某些控制台如何显示该对象之间存在区别。
  • @Rocket:我明白这一点。但这不是 jQuery 对象或 arguments 的工作方式。这就是各种控制台的工作方式,可能彼此不同。可能是我对你上面那句话的理解有点曲解了。

标签: javascript arrays object


【解决方案1】:

具体取决于控制台。对于 Chrome 的开发者控制台和 Firebug 中的自定义对象,您需要 lengthsplice 属性。 splice 也必须是一个函数。

a = {
    length: 0,
    splice: function () {}
}
console.log(a); //[]

但请务必注意,没有官方标准。

jQuery (v1.11.1) 在内部使用以下代码来确定一个对象应该使用for 循环还是for..in 循环:

function isArraylike( obj ) {
    var length = obj.length,
        type = jQuery.type( obj );

    if ( type === "function" || jQuery.isWindow( obj ) ) {
        return false;
    }

    if ( obj.nodeType === 1 && length ) {
        return true;
    }

    return type === "array" || length === 0 ||
        typeof length === "number" && length > 0 && ( length - 1 ) in obj;
}

请注意,可以有一个对象以数组 ([]) 的形式出现在控制台中,但它会被 jQuery 中的 for..in 循环迭代,或者在控制台中显示为对象的对象 ( {}) 但是会在 jQuery 中使用 for 循环进行迭代。

【讨论】:

  • 整洁!添加splice 属性did 将其显示为数组!
  • @Rocket,这就是为什么 jQuery 对象在控制台中看起来像数组,即使它们不是从 Array 继承的。
  • arguments.splice怎么设置成undefined呢?
  • 无论如何,这只是console 实现的错觉。毕竟,ECMAscript 中有 no Array(当我们忘记 TypedArrays 时)。
  • @Rocket,Firebug 中使用了特定类型的类型检查。不知道chrome用什么。对于自定义控制台,您可以在某些浏览器中检查 Object.prototype.toString.call(obj) === '[object Arguments]'
【解决方案2】:

当我们可以使用像 arguments 参数这样的数组时,我想到了同样的问题:

function arrayLike() {
  console.log(typeof arguments)
  console.log(arguments)
  console.log(Array.from(arguments))
}
arrayLike(1,2,3)

那么,让我们尝试创建我们自己的类数组对象:

let arrayLikeObject = {
  0: 1,
  1: 2
 }
 
 console.log(Array.from(arrayLikeObject))

显然,没有定义长度属性,所以我们的arrayLikeObject 只会返回一个空数组。现在,让我们尝试定义一个长度属性:

let arrayLikeObject = {
  length: 2,
  0: 1,
  1: 2
 }
 
 console.log(Array.from(arrayLikeObject))

如果长度设置不同怎么办?

let arrayLikeObject = {
  length: 1,
  0: 1,
  1: 2
 }
 
 console.log(Array.from(arrayLikeObject))
 // it will only return the value from first `0: 1`

let arrayLikeObject = {
  length: 5,
  0: 1,
  1: 2
 }
 
 console.log(Array.from(arrayLikeObject))
 // other 3 values will be printed as undefined

但是,我不想转换它...

您实际上想创建一个数组,而不是类似数组的对象。类数组对象必须像你说的那样转换:

Array.prototype.slice.call(arrayLikeObject)
// Or,
[].slice.call(arrayLikeObject)

如果你尝试在类数组对象上使用数组方法,那么你会得到类型错误:

let arrayLikeObject = {
  length: 5,
  0: 1,
  1: 2
 }

 console.log(arrayLikeObject.sort())

因此,要在 arrayLikeObject 上使用数组方法,我们需要将其转换为数组,就像我们在前面的示例中使用 Array.from 所做的那样。

否则,你只需要创建一个数组:

let arr = [1,2] // I don't mean, you don't know

其他注意事项:

你不能用它作为构造函数:

let arrayLikeObject = {
    length: 1,
    slice: function () {
      return 1
    }
}

console.log(new arrayLikeObject) // Type error

在下面的 sn-p 中,结果将是 [undefined],因为长度属性设置为 1,但没有 0 索引属性:

let arrayLikeObject = {
  length: 1,
  slice: function () {
    return 1
  }
}
console.log(Array.from(arrayLikeObject))

但如果将长度设置为 0,则结​​果将是一个空数组 [],因为我们告诉我们在这个类似数组的对象中没有任何值。

【讨论】:

  • 很高兴你能得到它的帮助
【解决方案3】:

这有什么用:extended array prototype,似乎他正在做你所做的事情并将原型创建为一个数组,但包括一个额外的方法(可能有效,也可能无效,我没有对此进行测试):

var MyArray = function() {
};

MyArray.prototype = new Array;

MyArray.prototype.forEach = function(action) {
    for (var i = 0, l=this.length; i < l, ++i) {
        action(this[i]);
    }
};

希望它在某些方面有所帮助。

【讨论】:

  • 添加了从Array.prototypeMyArray 的所有函数,但是对于arguments 和jQuery 对象,它们没有这些。
  • 这也增加了enumerable属性forEach
【解决方案4】:

看看这个:

var ArrayLike = (function () {

 var result;

 function ArrayLike(n) {

     for (var idx = 0; idx < n; idx++) {
         this[idx] = idx + 1;
     }

     // this.length = Array.prototype.length; THIS WILL NOT WORK !

 }


 // ArrayLike.prototype.splice = Array.prototype.splice; THIS WILL NOT WORK !


 // THIS WILL WORK !
 Object.defineProperty(ArrayLike.prototype, 'length', {

     get: function() {

         var count = 0, idx = 0;

         while(this[idx]) {
             count++;
             idx++;
         }
         return count;

     }

 });


 ArrayLike.prototype.splice = Array.prototype.splice;


 ArrayLike.prototype.multiple = function () {

     for (var idx = 0 ; idx < this.length ; idx++) {

         if (result) {
             result = result * this[idx];
         } else {
             result = this[idx];
         }
     }

     return result;
 };

 return ArrayLike
 })();

var al = new ArrayLike(5);

al.__proto__ = ArrayLike.prototype;

console.log(al.length, al.multiple(), al); 

这将在 Chrome 中显示:5 120 [1, 2, 3, 4, 5]

【讨论】:

    【解决方案5】:

    我认为this 是您正在寻找的。 重写 toString 函数。

    foo.prototype.toString = function()
    {
        return "[object Foo <" + this[0] +">]";
    }
    

    【讨论】:

    • 不完全。我想要的是当你console.log 它时,它显示为一个数组[]。在 Chrome 中,当你 console.log(arguments) 时,它看起来像一个数组,但实际上不是。
    【解决方案6】:

    您可能只想像继承基类一样继承 Array,因此您的对象只具有所有数组方法。你可以:

    function Thing() {
        // Adding a property
        this.whatever = 96;
    }
    Thing.prototype = Object.assign(Object.create(Array.prototype), {
        // Adding a method
        has42: function() {
            // Call inherited Array method
            return this.includes(42);
        }
    });
    

    其他地方:

    (function() {
        var test = new Thing();
        test.push(64, 69, 42, 96);
        console.log(test);
        var has = test.has42();
        console.log('should be true:', has);
        var i = test.indexOf(42);
        if (i >= 0)
            test.splice(i, 1);
        var has2 = test.has42();
        console.log('should be false:', has2);
        console.log(test);
     }());
    

    问题是,你是否应该这样做。根据对象的使用方式,某些代码可能会在使用普通数组制作副本时重新创建数组并丢弃原型链。这可能是也可能不是问题,具体取决于您对此的具体操作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-21
      • 2021-06-16
      • 1970-01-01
      • 2019-07-28
      • 1970-01-01
      • 1970-01-01
      • 2012-03-06
      • 1970-01-01
      相关资源
      最近更新 更多