构造函数创建对象
在学习原型前,首先要知道什么是构造函数,什么是实例。
如下代码所示: Foo是一个构造函数(构造函数一般首字母大写,除了这个其他跟普通函数没什么区别),child1和child2就是构造函数通过new所创建的实例,是一个对象。
function Foo() {} // 定义一个构造函数
const child1 = new Foo() // 创建一个实例child1
const child2 = new Foo() // 创建一个实例child2
prototype
每个函数都有一个
prototype属性,当我们声明函数的时候,这个属性就被自动创建了,并且这个属性的值是一个对象(也就是原型),只有一个属性constructor
注意,通过bind()方法所返回的函数不具有prototype属性
const test = Function.prototype.bind()
console.log(test.prototype) // undefined
提醒自己,只有函数有prototype属性,普通对象不存在这个属性,千万不要搞错,这点我之前老是记歪。。。
constructor
constructor是一个公有的不可枚举属性,这个属性指向的是函数本身,可以让实例对象知道是什么函数构造了它。
function Foo() {} // 定义一个构造函数
const child1 = new Foo() // 创建一个实例child1
console.log(child1.constructor == Foo) // true
__proto__
__proto__是每个对象都拥有的隐式原型属性,当我们在使用new创建实例时,该实例就自动拥有了__proto__属性,它让实例访问到构造函数的prototype属性。
function Foo() {} // 定义一个构造函数
const child1 = new Foo() // 创建一个实例child1
console.log(child1.__proto__ == Foo.prototype) // true
整体关系
从上述对构造函数(Foo),实例(child1),prototype,constructor,__proto__的介绍中可以总结一下:
- Foo通过prototype指向Foo.prototype
- child1.__proto__ 指向Foo.prototype
- Foo.prototype.constructor指向Foo 但是有一点需要注意的是:prototype属性本身也是一个实例对象,它也可以通过__proto__指向它所关联的原型
具体关系如下图所示:
从图中可以看出,除Object.prototype之外所有prototype的__proto__最终都指向Object.prototype,而它本身的__proto__属性则指向null。
原型链
当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么它会通过__proto__去prototype上寻找这个属性,而这个prototype又可以通过__ptoto__去访问它自己的prototype,再进行寻找,直到找到这个属性或找到null为止。这就是原型链的概念。
简单点来说,原型链就是一个对象查找属性的路线,只不过这条路线是通过__proto__所连接起来的。
为什么Function.__proto__ === Function.prototype?
首先,确认一点:Function.prototype 和 Object.prototype 是两个特殊的对象,它们是由引擎来创建的。
引擎先是创建了Object.prototype,然后再创建Function.prototype,并且通过__proto__将两者关联了起来。
所以为什么调用bind()方法返回的函数没有prototype属性?
因为bind()方法是在Function.prototype上的,而Function.prototype是由引擎创建的,引擎认为bind()方法返回的函数不需要prototype属性。
虽然Object.prototype和Function.prototype都是对象,但是它们不是Object创造的。因此:所有实例都是对象,但对象并不一定都是实例。
再来看看下面的代码:
console.log(typeof Function.prototype) // function
可以发现:Function.prototype是个函数,因此又可以得出一个结论:不是所有的函数都是new Function()产生的。
因此针对上述问题,其实就是Function.prototype是引擎自己创建的,是先有的Function.prototype才有的function Function(),而其他函数可以通过原型链找到Function.prototype,并且function Function也是一个函数,所以引擎就将Function.__proto__指向了Function.prototype
总结
- 函数的
prototype属性是一个对象,也就是原型。 - 实例的
__proto__指向原型,原型的__proto__指向原型的原型,连接起来就成了原型链。 - 访问对象属性时,现在自身寻找,找不到就通过
__proto__查找原型上有没有对应属性,直到找到null为止。 Function.prototype和Object.prototype是由引擎来创建的,比较特殊。- 除了这两个由引擎创造的对象之外,其他对象都是通过new构造出来的。
- Function.prototype.bind()没有prototype属性是因为
Function.prototype是引擎创造的,它认为其不需要prototype属性。