README
原型链
原型链是一种机制,指的是 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