Heim > Fragen und Antworten > Hauptteil
var a = [1,2,3];
a.constructor.prototype
结果为[]
,但a
还是可以调用concat
方法。按照我的理解,如果想要调用concat
方法,必须要先继承concat
方法,那么a.constructor.prototype
的结果应该为类似这种形式的对象{concat:function(){}}
,我的理解哪里出了问题?
除此之外,我用的chrome浏览器还实现了这种方法__defineGetter__
,这又是怎么做到的?我看原型链上并没有此类方法,所以它应该不是在原型链上添加的吧:
var s = ''
s.constructor.prototype
天蓬老师2017-04-10 15:21:33
先说结论:
因为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的时候,不会显示它的额外属性,但这不代表这些属性不存在。
大家讲道理2017-04-10 15:21:33
例子:
[] instanceof Array // true
[].constructor === Array // true
结论:
[] 是Array的实例
类似于这样的"__proto__" 都是浏览器里暴露出来的方法,开发者可以用来调试等