首页 > 数组实例的方法从哪继承来的?

数组实例的方法从哪继承来的?

var a = [1,2,3];
a.constructor.prototype    

结果为[],但a还是可以调用concat方法。按照我的理解,如果想要调用concat方法,必须要先继承concat方法,那么a.constructor.prototype的结果应该为类似这种形式的对象{concat:function(){}},我的理解哪里出了问题?

除此之外,我用的chrome浏览器还实现了这种方法__defineGetter__,这又是怎么做到的?我看原型链上并没有此类方法,所以它应该不是在原型链上添加的吧:

var s = ''
s.constructor.prototype

例子:

[] instanceof Array   //  true

[].constructor === Array  // true

结论:

[] 是Array的实例

类似于这样的"__proto__" 都是浏览器里暴露出来的方法,开发者可以用来调试等


先说结论:
因为Array.prototype是一个array,浏览器或者node.js在显示一个array的时候,不会显示它的额外属性,但这不代表这些属性不存在。
你可以试试

Array.prototype.hasOwnProperty('concat'); //true

至于__xxx__这种格式的属性,都如@qianjiahao 所说是浏览器内部暴露出来的,为了调试方便而存在,在正式产品中是不应当使用的。


Array是一个内建类型,这时候就先看一下标准中是怎么说的:

15.4.3.1 Array.prototype
The initial value of Array.prototype is the Array prototype object (15.4.4).

15.4.4 Properties of the Array Prototype Object
The Array prototype object is itself an array; its [[Class]] is "Array", and it has a length property (whose initial value is +0) and the special [[DefineOwnProperty]] internal method described in 15.4.5.1.

Array.prototype本身就是一个array,且它的length属性为0,所以你

var a = [1,2,3];
a.constructor.prototype

得到的自然是一个[]。
那么concat那些方法在哪呢?


15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , … ] ] ] )

这一节的标题就说明了,concat方法确实是在Array.prototype中的,我们试一试:
console.log(Array.prototype.hasOwnProperty('concat')),得到的结果果然是true。


前面说Array.prototype是一个array,以及Array.prototype具有concat这个属性这两件事,联想到是不是任何一个array都直接拥有concat这个属性呢,我们再试试:
[].hasOwnProperty('concat'),结果却是false,这说明并不是任何array都直接拥有concat这个属性的。于是得到一个很合理的结论:Array.prototype是一个很特殊的array。现在再回到标准中看一看:

15.4.5 Properties of Array Instances

Array instances inherit properties from the Array prototype object and their [[Class]] internal property value is "Array". Array instances also have the following properties.

15.4.5.1 [[DefineOwnProperty]] ( P, Desc, Throw )
15.4.5.2 length

普通的array只拥有两个属性,一个是[[DefineOwnProperty]]这样的内部方法,我们在JavaScript语言中访问不到,另一个则是length属性了。


所以说Array.prototype是一个特殊的array,它除了拥有和其他array一样的length属性之外,还拥有一些concat/shift/pop/push这些方法。而其他array的原型[[Prototype]]正是这个特殊array。
说到这里,我们遇到了一个鸡生蛋蛋生鸡的问题:

Array.prototype也是一个array(下面我称之为数组原型对象APO),而想要创建一个array,无论是显示调用Array构造函数还是采用数组字面量(array literal)的形式,都会有将新创建的array的[[Prototype]]指定为APO的过程。也就是说APO这个array的时候,我们需要先有一个APO。

这个问题有两种解决方案:
一、先指定Array.prototype为null,调用Array构造函数创建APO,此时APO的原型为null,然后为APO添加concat等各种属性,再将Array.prototype指向APO,好了,bootstrap完毕。
二、更方便了,JavaScript引擎内部创建一个对象APO,添加concat这些属性,直接设定APO的[[Class]]为Array,然后指定Array.prototype为APO,即可。

用下面这段代码来看看JavaScript内部到底采用的是哪种方案:

console.log(Array.prototype instanceof Array); //false

这说明是第二种方案了,也就是说APO虽然是一个array,但它并不是使用Array构造函数创建的,而是在引擎内部hard coded。

The value of the [[Prototype]] internal property of the Array prototype object is the standard built-in Object prototype object (15.2.4).

这一句也说明了APO的[[Prototype]]并不是Array.prototype,而是一个语言内置的Object prototype object。

console.log(Array.prototype.__proto__ === Object.prototype); //true
console.log(Array.prototype instanceof Object); //true

其实还可以试一试,Number、Array、RegExp等这些内置对象,他们的构造函数的prototype属性的[[Prototype]]也都是Object.prototype:

console.log(Number.prototype.__proto__ === Object.prototype); //true
console.log(RegExp.prototype.__proto__ === Object.prototype); //true
console.log(String.prototype.__proto__ === Object.prototype); //true
console.log(Function.prototype.__proto__ === Object.prototype); //true

不过Object.prototype的[[Prototype]]却是null:

console.log(Object.prototype.__proto__); //true

这也是在标准中有明确规定的:

15.2.4 Properties of the Object Prototype Object

The value of the [[Prototype]] internal property of the Object prototype object is null, the value of the [[Class]] internal property is "Object", and the initial value of the [[Extensible]] internal property is true.


到这里可以总结一下:

var a = [1,2,3];
a.constructor.prototype //这确实是一个array[],不过它是一个特殊的array

至于你看不到那些属性,恰恰是因为Array.prototype是一个Array,浏览器或者node在显示一个array的时候,不会显示它的额外属性,但这不代表这些属性不存在。

【热门文章】
【热门文章】