[wtfjs] 对象和数组原型的长度

  |   0 评论   |   0 浏览   |   Erioifpud

https://wtfjs.com/wtfs/2016-03-10-Object-And-Array-Prototype-Length

Object.create(Array).length === 1 //true

And

Object.keys(Object.create(Array)).length === 0 //true

代码片段一

首先,Object.create(Array)会以 Array这个构造函数为原型,创建出新的对象,在文章里称他为对象 a

已知 a.length为 1,那么先通过 getOwnPropertyNames检查 a本身的键,可以看到 arr对象本身是没有键的,那么 length实际上是委托在原型对象上的。

那么问题就变成了 Array.length为什么等于 1,这个特性可以查看 ECMAScript 文档,这里摘取一部分:

Properties of the Array Constructor
Array 构造函数的属性

The value of the [[Prototype]] internal property of the Array constructor is the Function prototype object (15.3.4).
Array 构造函数的内部属性 [[Prototype]] 的值是 Function 原型对象(15.3.4)。

Besides the internal properties and the length property (whose value is 1), the Array constructor has the following properties:
除了内部属性和 length 属性(其值为 1),Array 构造函数还有以下属性:

文档里写明了 Array中存在 length,并且值为 1。

代码片段二

到这里,第一段代码解释完了,第二段代码先通过 Object.keys得到,接下来看一下 Object.keys的标准:


When the keys function is called with argument O, the following steps are taken:

当使用参数 O调用keys这个函数时,进行以下步骤:

  1. If the Type(O) is not Object, throw a TypeError exception.

如果 O的类型不是对象,那么就抛出一个TypeError异常。

  1. Let n be the number of own enumerable properties of O

将对象 O的可枚举属性个数赋值给 n

  1. Let array be the result of creating a new Object as if by the expression new Array(n) where Array is the standard built-in constructor with that name.

通过 new Array(n)创建出一个新的数组,赋值给 array

  1. Let index be 0.

index赋值为 0。

  1. For each own enumerable property of O whose name String is P

    遍历对象 O的每一个可枚举属性,用 P表示:

    1. Call the [[DefineOwnProperty]] internal method of array with arguments ToString(index), the PropertyDescriptor {[[Value]]: P, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.

    使用字符串形式index、属性描述符 {[[Value]]: P, [[Writable]]: **true**, [[Enumerable]]: **true**, [[Configurable]]: **true**}false作为参数,调用 array的内部方法 [[DefineOwnProperty]]

    1. Increment index by 1.

    index增加 1。

  2. Return array.

返回 array

If an implementation defines a specific order of enumeration for the for-in statement, that same enumeration order must be used in step 5 of this algorithm.

如果具体实现中为 for-in 语句定义了特定的枚举顺序,那么在本算法的第 5 步中也必须使用相同的枚举顺序。


其实我们只需要关注两点:

  1. 遍历的属性的可枚举
  2. 遍历的属性是对象 O

这也就说明了 Object.keys并不会在对象 O的原型链上进行查询,那么回顾一下文章的第一段,通过 getOwnPropertyNames检查 a,发现 a本身并没有键,所以Object.keys(arr)的返回值是一个空数组,空数组对象长度自然是 0。

陷阱

这两段代码中有一个误导人地方,那就是 Object.create(Array),会让人觉得创建出的实例是数组,实际上并不是。

把Array换成Object、Function等其他的类型可能就不会将这里的 length与数组长度的 length混淆了。

这里使用的原型对象是 Array函数,也就是说创建出的对象 a他的 __proto__不是数组原型对象,而是数组构造函数

let a = Object.create(Array)

a instanceof Function          // true
a instanceof Array             // false
a instanceof Array.constructor // true

那么就可以参考 MDN 的 Function 页面了:

length 是函数对象的一个属性值,指该函数有多少个必须要传入的参数,即形参的个数。

形参的数量不包括剩余参数个数,仅包括第一个具有默认值之前的参数个数。

与之对比的是,arguments.length 是函数被调用时实际传参的个数。

这里说得很清晰,函数中的length属性,指的其实是函数需要的参数个数(不是实际传入的个数),并且不包括剩余参数,比如说:

function foo (a, b) {}
foo.length // 2

function bar (a, ...b) {}
bar.length // 1

现在重新看回代码片段一,就能有新的理解了,为了方便解释,我把代码一拆成了如下形式:

let a = Object.create(Array)
a.length
  1. a中不存在 length
  2. 所以得在原型链上找,原型链向上第一个对象就是 Array这个构造函数
  3. Array这个函数接受一个参数作为 size,创建出长度为 size的数组。
  4. 所以 Array实际需要的参数个数为 1Array.length为 1。

标题:[wtfjs] 对象和数组原型的长度
作者:Erioifpud
地址:https://blog.doiduoyi.com/articles/1606378597414.html

评论

发表评论