javascript中的Array.length与arrayinstance.length

时间:2019-03-05 01:54:24

标签: javascript arrays

我对Array.length属性(即length函数对象上名为Array的属性)和array_instance.length(即名为{{ 1}}的数组对象实例)

enter image description here

那么两个length属性之间有什么区别?我们何时/不应该使用它们?

编辑1:

length对象上还有一个length属性。我感到很困惑。

enter image description here

编辑2

只需画出更清晰的图片,以下是我发现的不同Array.prototype属性

enter image description here

编辑3

这是我在评论部分中提出的后续问题之一,但是我认为该问题对于充分理解length属性很重要,因此我在此处将其粘贴粘贴在主要部分中

后续问题:

lengthArray.prototype.hasOwnProperty('length')返回Array_instance.hasOwnProperty('length'),这意味着有两个true属性,一个在length上,一个在array_instance对象上,而Array.prototype超越了array_instance.length

6 个答案:

答案 0 :(得分:26)

函数具有.length属性,该属性对应于它们期望的参数数量。例如:

const unary = (a) => {

}

const binary = (a, b) => {

}

console.log(unary.length);
console.log(binary.length);

所以Array构造函数的长度为1,因为它希望将一个参数(即数组的大小)传递给它。

数组对象还具有.length属性,除了具有相同的名称外,它们是无关的。该属性表示当前数组的大小。

答案 1 :(得分:12)

第一部分已经回答,Array构造函数是一个函数,并且函数具有.length属性。

第二个Array.prototype.length有点模糊...

Array.prototype实际上是一个数组:

console.log(Array.isArray(Array.prototype)); // true
Array.prototype.push('hello'); 
console.log(Array.prototype.length); // 1
console.log(Array.prototype[0]); // "hello"

为什么它是一个数组?因为specs这么说:

  

Array原型对象是Array奇异对象,并具有为此类对象指定的内部方法。它具有一个length属性,其初始值为0,其属性为{[[[Writable]]:true,[[Enumerable]]:false,[[Configurable]]:false}。

还有一条注释说明它是出于与previous versions of the specs的兼容性原因。

关于为什么被设计为数组?我没有一个好主意...

答案 2 :(得分:8)

我真的认为很多其他答案都涵盖了此处所需的所有内容,但由于OP似乎没有他们所看到的明确答案,因此我将尽一切努力将所有内容都列出来-但要尽可能清楚我可以-为了澄清。 (很抱歉,如果有人认为我在“窃取”他们的答案,我向您保证这不是故意的,在我输入此内容时,我刻意不看他们,但我确实阅读了其中的大部分内容,甚至对a几个。)

Array.length

上面已经很好地介绍了这一点。 Array是本机JS函数,可用于创建数组。它不那么简单,而是简单地定义一个数组文字(据我所知,没有理由更可取),但是您可以这样做,而不是var a = [1,2,3]

var a = Array(1,2,3);
console.log(a);

请注意,您不想要执行此操作以创建单例数组,当您恰好提供一个恰好是整数的参数时,会有一种非常特殊的情况:

var a = Array(5);
console.log(a);

尽管在SO所使用的任何JS控制台实现中显示的似乎都是5个undefined值的数组,但实际上并不是所创建的(也不是在当前版本的Chrome中显示的)。我正走出正题,但我将带您回到疯狂的Kyle Simpson's excellent walkthrough

无论如何,由于Array是一个函数,正如其他人已经观察到的那样,它具有length属性as all functions do。我真的不确定为什么将其评估为1-对于用户定义的函数,它是函数正式声明的参数数量,但由于Array像所有本机JS函数一样,是'它实际上是用JS实现的,所以我无法告诉您该实现的实际工作方式。显然,它可以接受任意数量的参数,因此是一个相当“奇特”的功能。我不认为Array.length永远不会有用,无论给它分配了什么值,但是即使规范将其保留为开放状态,大多数实现似乎都为1。 (我还不是一个专门的技术人员,不知道这是在其中实际定义的还是要留给实现的。)

arrayinstance.length

这只是我们一直知道和使用的数组的功能。所有JS数组都具有此属性-请注意,尽管它是属性而不是方法(也就是说,它不是函数),但随着数组的变长或变短,它仍会“自动更新”:

var a = [1,2,3];
console.log(a.length);
a.push(4);
console.log(a.length);
a.pop();
a.pop();
console.log(a);
console.log(a.length);

尽管我说过,Javascript的本机构造函数不是用JS本身实现的,但是您可以通过定义getter(至少从ES5开始)来实现这种行为。

Array.prototype.length

要完全解释Array.prototype(和类似的对象)是什么,将使我深入了解Javascript的对象系统如何工作。足以在这里说,与基于类的语言不同(尽管class语法允许我们经常假装确实如此,但JS在即使在ES6中也没有类),具有通常所谓的“继承”概念的“继承”概念。在JS的版本中(有时称为“原型继承”),每个对象都有一个引用了其他对象的“内部原型”,如果发生这种情况,那么JS会发生这种情况。引擎将查看该“原型对象”的属性,并使用其值代替。

这实际上是一个非常简单的机制,但是语言中有很多东西使这一点感到困惑,其中之一就是函数(也是对象)具有称为prototype的属性的事实- not 指向实际的“原型”对象,如果在函数对象上引用了不存在的属性,则该对象将被查询。 (普通函数foo拥有Function.prototype是它委托的对象,而不是foo.prototype。)但是,如果声明函数foo,则称为{{1 }}已创建-基本上是一个空的,未描述的对象。它的意义在于,如果函数foo.prototype用作“构造函数”,也就是说,如果您通过调用foo来创建对象,那么new foo()将成为JS看起来的对象由foo.prototype构造的对象是否碰巧无法进行属性查找。

这就是为什么至少在ES6之前的代码中您经常看到这种模式的原因:

foo

尽管出现,但function foo(a,b) { this.a = a; this.b = b; } foo.prototype.add = function() { this.a = this.a + this.b; } var myFoo = new foo(1,2); console.log(myFoo.a); myFoo.add(); console.log(myFoo.a); myFoo.add(); console.log(myFoo.a); console.log(myFoo.hasOwnProperty("add"));在此示例中实际上没有方法myFoo-由最后的add证实。但是,当JS引擎无法找到属性时,它将转到console.log的“内部原型”,恰好是myFoo。这就是为什么该方法有效的原因,就像对foo.prototype构造的任何对象一样。

无论如何,这导致了一个事实,即可以通过调用foo(虽然我没有在上面使用new Array运算符来构造(虽然几乎从来没有)过的Arrays,但是我可以这样做,这是没有区别的情况),因此委托给new。您知道并喜欢的所有那些数组方法并不“真正”存在于您调用它们的数组上:

Array.prototype

因此,数组方法之所以起作用,是因为这些方法实际上是在var a = [1,2,3]; a.push(4); console.log(a); console.log(a.hasOwnProperty("push")); console.log(Array.prototype.hasOwnProperty("push"));对象上找到的,如果在数组本身上查找不成功,则所有数组都将委托该数组方法进行属性/方法访问。 (这就是为什么如果您在MDN上查找它们中的任何一个,页面顶部总是显示Array.prototype,因为这是方法“真正”存在的地方。)

对此进行了激烈的演示(请不要在生产代码中这样做!)

Array.prototype.<method_name>

但是我要结束反高潮了。上面的内容适用于数组方法-但// you're used to doing this, and it works: [1,2].push(3); console.log("that went fine"); // vandalism on a grand scale! Array.prototype.push = undefined; // now you can'tse the "push" method anymore! [1,2,3].push(4);数组属性不是一个方法。如上所述,它只是一个“普通”(非函数)属性,其“神奇地”表现有点像函数调用。正如在OP中所观察到的,length属性访问不要委托,就像上面显示的方法调用一样,该属性本身存在于每个数组中。

那为什么.length本身仍然具有Array.prototype属性?好吧,length实际上是一个数组。实际上,这不是唯一的事情:

Array.prototype

请注意,Array.prototype.push(1); console.log(Array.prototype); Function.prototype();最终以Array.prototype.push(1)为单例数组Array.prototype。因此[1]就像是一个空数组(它并不完全相同,因为它具有上面提到的所有方法,可以直接在对象本身上访问,而“普通”空数组则不能)。使用Array.prototype时,尽管调用它不会输出任何内容,但没有引发Function.prototype的事实证明它确实是一个 函数(它实际上是“无操作” ”功能,例如TypeError,但它又直接带有各种方法-每个功能都可以使用的方法,例如function() {}.call)。

无论如何,为了缩短题外距离,因为就普通数组属性而言,.bind至少是一个空数组,这解释了为什么它具有Array.prototype属性,以及为什么等于0。

我希望这可以使事情变得清晰,并演示一些更有趣的Javascript部分。

答案 3 :(得分:6)

免责声明: 我试图显示构造函数和实例的一般工作原理。但是实际上,它们在不同的构造函数之间有巨大的差异。

已使用规范指定的值设置了任何构造函数的长度。特别是大多数设置为1:

Number.length // 1
Object.length // 1
Function.length // 1
Array.length // 1
// etc.

类似地,

Array.constructor.length // 1
// etc.

就像@Kaiido在评论中指出的那样,您可以看到设置了一些构造函数长度:

Document.length // 0
Int8Array.length // 3
Date.length // 7 

您可能还会注意到实例的长度为undefined,因为它们没有设置。 -不过超出范围了。

let d = new Date
d.length === undefined // true

请参阅底部的相关参考文献。

但是,当您拥有它的一个实例时,便会为其创建一个新值:

typeof new Array === typeof Array.prototype // true
typeof new Function === typeof Function.prototype // true
// etc.

因此,当您使用实例时,它没有设置长度值,因为它没有任何参数:

let arr = new Array // Array.prototype
arr.length === 0 // true

但是当您使用带有参数的实例时,则具有具有参数值的length属性

let arr = new Array(20)
arr.length === 20 // true
let func = function(a1,a2,a3,a4){}
func.length === 4 // true
// etc.

所以现在,您一直想知道为什么构造函数的长度值等于1?

这是因为规范最初将值设置为1。

  

每个内置的Function对象(包括构造函数)都有一个length属性,其值是整数。除非另有说明,否则该值等于函数说明的子标题中显示的最大命名参数,包括可选参数。

     

Object构造函数的[[Prototype]]内部插槽的值是固有对象%FunctionPrototype%。

     

除了length属性(其值为1)之外,

请参阅以下参考文献:

Standard built in objects

19.1.2 Properties of the Object Constructor

19.2.2 Properties of the Function Constructor

此外,您可以看到原型的长度为0,在前面的示例中您已经知道为什么。

不过,这里仅是说明:

19.2.3 Properties of the Function Prototype Object

还有一些长度设置不同的构造函数。这超出了此答案的范围。不过,以下是日期构造函数的参考:

20.3.3 Properties of the Date Constructor

因此,完全由规范决定如何定义它们。

答案 4 :(得分:5)

Array.length

Array是构造函数,这意味着其类型为"function"。您可以尝试检查控制台。

typeof Array //"function"

根据MDN

  

length属性指示function期望的参数数量。

由于Array函数期望使用单个参数,因此Array.length = 0

array_instance.length

  

作为对象length的实例的对象的Array属性设置或返回该数组中元素的数量

我们知道数组实际上是对象,因此对象可以具有属性。属性length在数组的实例上。
现在,您可能会问第二个问题,为什么我们不能使用lengthObject.keys循环来获得数组的for..in属性。答案是因为此属性不是Enumerable

let arr= [];

//this.property 'length2' will not be shown in for..in or Object.keys();
Object.defineProperty(arr,'length2',{
  value:'length2 for test',
  enumerable:false
})

//this property 'x' will be displayed in for..in loop and Object.keys()
Object.defineProperty(arr,'x',{
  value:'length2 for test',
  enumerable:true
})


console.log(Object.keys(arr)); //["x"]

Array.prototpe.length

根据DOCS

  

Array.prototype.constructor的初始值为标准内置Array   Array原型对象本身就是一个数组。其[[Class]]是“数组”,并且它具有length属性(其初始值为+0)构造函数

实际上Array.prototype是一个数组。并记住数组始终是对象。因此它可以具有属性。数组的方法以key:value的形式存储。并且该数组中没有元素,因此Array.prototype.length返回0。如果您在其中push()插入了一些元素,则会将其视为数组。

console.log(Array.prototype.length) //0

console.log(Array.isArray(Array.prototype)) //true

//adding element to array
Array.prototype.push('x') 

console.log(Array.prototype.length) //1

正如我在第二部分中所述,您可以通过设置Object隐藏enumerable:false的属性。所有方法都是Array.prototype的键,但现在显示在for..in循环中。

答案 5 :(得分:3)

Array.length 对于数组中的属性数目或instance of type Array对象的length属性,设置或返回该数组中元素的数目。

Array.prototype.length 数组中继承的属性数。当您检查Array.length时,实际上是在检查Array.prototype.length