我对Array.length
属性(即length
函数对象上名为Array
的属性)和array_instance.length
(即名为{{ 1}}的数组对象实例)
那么两个length
属性之间有什么区别?我们何时/不应该使用它们?
编辑1:
在length
对象上还有一个length
属性。我感到很困惑。
编辑2
只需画出更清晰的图片,以下是我发现的不同Array.prototype
属性
编辑3
这是我在评论部分中提出的后续问题之一,但是我认为该问题对于充分理解length
属性很重要,因此我在此处将其粘贴粘贴在主要部分中
后续问题:
length
和Array.prototype.hasOwnProperty('length')
返回Array_instance.hasOwnProperty('length')
,这意味着有两个true
属性,一个在length
上,一个在array_instance
对象上,而Array.prototype
超越了array_instance.length
?
答案 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)之外,
请参阅以下参考文献:
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
是构造函数,这意味着其类型为"function"
。您可以尝试检查控制台。
typeof Array //"function"
根据MDN
length
属性指示function
期望的参数数量。
由于Array
函数期望使用单个参数,因此Array.length = 0
作为对象
length
的实例的对象的Array
属性设置或返回该数组中元素的数量
我们知道数组实际上是对象,因此对象可以具有属性。属性length
在数组的实例上。
现在,您可能会问第二个问题,为什么我们不能使用length
或Object.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"]
根据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