JavaScript中的类数组对象

32
我想知道jQuery如何构建其类数组对象。我试图弄清楚的关键是它如何管理将其解释为数组并显示为数组。我知道这与length属性有关,但经过一番尝试,我仍然不太明白。我知道这与下面示例中的常规类数组对象没有技术上的优势。但我认为在用户测试和调试时,这是一个重要的语义元素。
一个常规的类数组对象。
function foo(){
    // Array like objects have a length property and it's properties use integer
    // based sequential key names, e.g. 0,1,2,3,4,5,6 just like an array.
    this.length = 1;
    this[0] = 'hello'
}
// Just to make sure add the length property to the prototype to match the Array 
// prototype
foo.prototype.length = 0;

// Give the Array like object an Array method to test that it works     
foo.prototype.push = Array.prototype.push

// Create an Array like object 
var bar = new foo;

//test it 
bar.push('world');

console.log(bar);
// outputs 
{ 0: 'hello',
  1: 'world',
  length: 2,
  __proto__: foo
}

而 jQuery 则会输出

var jQArray = $('div')

console.log(jQArray);

// outputs
[<div></div>,<div></div>,<div></div>,<div></div>]

如果你运行

console.dir(jQArray)

// Outputs

{ 0: HTMLDivElement,
  1: HTMLDivElement,
  2: HTMLDivElement,
  3: HTMLDivElement,
  4: HTMLDivElement,
  context: HTMLDocument,
  length: 5,
  __proto__: Object[0]
 }

jQuery对象的原型特别有趣,因为它是Object而不是预期的jQuery.fn.init,同时[0]指示着某些内容,因为这就是当你...时得到的内容。

console.dir([])
// outputs Array[0] as the object name or Array[x] x being the internal length of the
// Array
我不知道jQuery是如何将它的原型设置为Object[0]的,但我猜答案可能就在那里。有人有想法吗?

我不知道jQuery是如何将它的原型设置为Object[0]的,但我猜答案可能就在那里。有人有想法吗?


我不确定,但是你不能创建一个对象,然后将其原型设置为Array.prototype吗?这样它就成为了类似数组的对象?或者不行? - John Strickler
据我所知,这将使其成为普通数组。但我也想避免这种情况,因为我不想将所有数组方法添加到我的对象中,以避免混淆。由于某些数组方法实际上会返回一个新数组,因此当用户使用其中的某些方法时,附加到我的类似数组的对象的所有其他方法都将丢失。 - AshHeskes
2个回答

43

对象必须具有 lengthsplice

> var x = {length:2, '0':'foo', '1':'bar', splice:function(){}}
> console.log(x);
['foo', 'bar']

顺便提一下,Object[0] 作为原型的原因也是完全相同的。浏览器之所以将原型本身视为数组,是因为:

$.prototype.length == 0;
$.prototype.splice == [].splice;

1
如果您不需要将对象作为数组使用console.log()打印,那么就不需要使用splice。长度和像0,1这样的属性足以使用数组方法。 - daremkd
@zyklus 你知道这是否是ECMA规范的一部分(类数组对象),还是浏览器的优化吗? - Usagi
@Usagi - 据我所知,这与ECMA脚本无关,只是一个随机的开发工具。在JS中有很多“类似数组”的对象,无论是语言内置的还是用户创建的,因此如果它们实现了一些最小的Array子集,则将它们显示为数组是很有用的,这正是开发人员选择的方式。 - user578895

0

像这样吗?

function foo() {
  this.push('hello');
}
foo.prototype = [];

var bar = new foo();
console.log(bar.length); // 1
console.log(bar); // ["hello"]

这与我给@JohnStrickler的回复具有相同的效果。我不想将所有Array方法添加到我的Object中,以避免混淆,因为某些数组方法实际上会返回一个新的Array。因此,当使用它们时,我附加到对象的任何其他方法都将丢失。 - AshHeskes
jQuery对象使用数组作为其原型。我认为你可以重新定义这些方法,使它们返回新的foo对象,而不是常规数组。 - Andrey M.
5
@Andrey M. - 请不要随意发表你不确定的言论。jQuery并没有把Array作为它的原型。它只是使用了Arraypushsortsplice方法。 - user578895
2
我再次查看了jQuery对象的原型链,但没有找到任何地方定义了Array。 - AshHeskes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接