JS个人学习(6)——原型与原型链

262 阅读3分钟

构造函数创建对象

在学习原型前,首先要知道什么是构造函数,什么是实例

如下代码所示: 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)prototypeconstructor__proto__的介绍中可以总结一下:

  • Foo通过prototype指向Foo.prototype
  • child1.__proto__ 指向Foo.prototype
  • Foo.prototype.constructor指向Foo 但是有一点需要注意的是:prototype属性本身也是一个实例对象,它也可以通过__proto__指向它所关联的原型

具体关系如下图所示:

无标题-2021-08-07-2057.png

从图中可以看出,除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.prototypeFunction.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属性。