原型链
原型链是一种机制,指的是JavaScript每个对象包括原型对象
都有一个内置的[[proto]]属性指向创建它的函数对象的原型对象,即prototype属性。原型链的存在,主要是为了实现对象的继承
。
函数即对象
- js分为函数对象和普通对象,每个对象都有__proto__属性,但是只有函数对象才有prototype属性
- Object、Function都是js内置的函数, 类似的还有我们常用到的Array、RegExp、Date、Boolean、Number、String
__proto__
JavaScript在创建对象的时候,都会有一个[[proto]]
的内置属性,用于指向创建它的函数对象的prototype
。原型对象也有[[proto]]
属性。因此在不断的指向中,形成了原型链。
prototype原型对象
当定义一个函数对象的时候,会包含一个预定义的属性,叫prototype
,这就属性称之为原型对象。
只有函数有prototype属性,也就是说__proto__
的值是它所对应的原型对象,是某个函数的prototype
constructor
实例的constructor
属性指向它的构造函数
原型对象prototype上的预定义的constructor
属性,用来引用它的函数对象。这是一种循环引用。
经常会有下列的写法,加constructor
是因为重写了原型对象,constructor属性就消失了,需要自己手动补上。
function F(){};
F.prototype = {
constructor : F,
doSomething : function(){}
}
JavaScript在创建对象的时候,都会有一个[[proto]]
的内置属性,用于指向创建它的函数对象的prototype
。原型对象也有[[proto]]
属性。因此在不断的指向中,形成了原型链。
原型链的内存结构
原型链终极探索图
注意几个指向问题,下文详细分析
- Foo.prototype和Function.prototype指向Object.prototype
- Foo()的__proto__指向Function.prototype
- 别的函数对象constructor指向Function,Function的constructor指向自身
普通构造函数的实例对象原型链分析
这里的myfun()相当于上图的Foo()函数
function myfun(x,y){
this.x = x
this.y = y
this.sum = function(){
return x+y
}
}
myfun.prototype.sum = function(){
return this.x+this.y
}
myobj = new myfun(1,2)
console.log(myobj)
非Function类型函数对象的原型链分析
常用到的Array()、RegExp()、Date()、Boolean()、Number()、String()这些构造函数其实都是有Function构造而来,从他们的constructor属性的指向可以证明,这里我分析一下Array()的原型链.
可以看到Array._proto_是一个function,也就是说Array._proto_指向的Function.prototype是一个function,其他普通对象的原型却是一个Object
Array.__proto__ -> Function.prototype
Function.prototype是一个函数类型的对象
Function.prototype.__proto__指向最顶层的Object.prototype
console.log(Array)
Function函数对象的原型链
在ES6标准中,Function 对象有两个属性:
-
length 值为1,这个属性的特性为
{ [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }
,即不可覆盖,不可被for...in
遍历,但可以通过Object.defineProperty
修改它的上面这些特性 -
prototype 为原型对象,(见ES最新标准说明 Function.prototpye)它跟一般函数的
prototype
的区别在于-
它不可写,不可配置,不可遍历。 即它永远指向固定的一个对象,且是其他所有函数的原型对象,所有函数本身的
__proto__
指向它。 -
它是一个函数。 一般函数的
prototype
是一个带有constructor
属性的普通对象,但Function
的prototype
是一个函数对象(built-in function object
),是js
中唯一一个默认prototype
为函数的对象
-
它不可写,不可配置,不可遍历。 即它永远指向固定的一个对象,且是其他所有函数的原型对象,所有函数本身的
函数和对象都有__proto__
属性,指向构造函数的prototype
属性所指向的对象,即它的原型对象。
而函数的__proto__
属性(并非它的原型对象prototype
上的__proto__
属性)指向Function.prototype
,所以Function.prototype
上的属性和方法都会被函数对象(function object)所继承。
似乎进入了“鸡生蛋和蛋生鸡”的哲学范畴Object
本身是构造函数,继承了Function.prototype
;Function
也是对象,继承了Object.prototype
。
Function.__proto__ === Function.prototype // true
Object.__proto__ === Function.prototype // true
Object.prototype.__proto__ === null // true
Function.prototype.__proto__ === Object.prototype // true
Object.prototype === Object.__proto__ // false
Object instanceof Function // true
Function instanceof Object // true
完全没必要去纠结鸡生蛋还是蛋生鸡的问题,之所以Function.__proto__ === Function.prototype
,是为了表明Function
作为一个原生构造函数,本身也是一个函数对象(由自己构造出的实例),让 Function
这个构造函数 可以获取定义在 Object.prototype
上的方法。
console.log(Function)
总结
属性
万物皆对象
注:全部为自己的看法,可能与标准的说法有出入,只是方便自己理解,我快晕了
js里万物皆为对象,最顶层为Object对象是一切的原型(起源) 注意这里的Object不是构造函Object()是一个切实的原型对象
Object(),Function(),Array(),String()这些方法就像我们自定义的方法一样(只不过是js语言帮我们内置创建了这些方法来创建特殊的类,本质都是Object)
每个方法都有(可以说是创建了)自己的prototype(原型对象),然后这些原型对象的_proto_又都指向了最顶层的Object原型对象(由此可以说明是顶层对象创建了每个方法的原型对象,也就是说方法类的原型对象是顶层Object类的原型对象创建的实例一样)
其中的Function方法较为特殊,Object(),Array(),String()这些方法都是有Function方法构造而来
Function构造函数本身就是Function的实例(像是自己构造自己得到的函数对象),所以_proto_指向Function的原型对象.
再看一张原型链的铁三角图
注意:一个函数存在两种状态
- 作为构造函数时,可以去new一个实例对象,这个对象可以是普通对象也可以说函数对象(Function就可以构造函数对象)
- 作为被构造的函数(实例)对象时,自己的__proto__要指向构造自己的构造函数的prototype(就是指向原型对象)
参考文章
https://juejin.im/post/5cc99fdfe51d453b440236c3
https://segmentfault.com/a/1190000011880268
https://segmentfault.com/a/1190000005363885
https://juejin.im/post/5b3798f851882574c105c51c
https://github.com/creeperyang/blog/issues/9
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。