【JavaScript】原型对象、对象原型、原型链及其查找规则

一、原型对象prototype

  • 在ES6之前,js通过构造函数来创建一个类。js中每一个构造函数都有一个prototype属性,它是一个对象,称为原型对象。这个对象的所有属性和方法,都会被构造函数所拥有。
  • prototype对象是为了共享方法,减少内存浪费。

我们原来是把所有属性和方法都写在构造函数里,这样每创建一个实例对象,就会把所有属性和方法都创建一遍,就会开辟一个新的空间去存储对应的方法,但是这些方法其实是共有的,所有对象都一样的,这样无疑是浪费了很多内存空间(左边示例)。
而现在我们把公共属性放到构造函数来,把不变的方法(函数),直接添加给prototype对象,这样这个类的所有实例对象就可以共享这些方法了,同时减少了内存的浪费,这个方法只在prototype对象里面存储了一次(右边示例)。
在这里插入图片描述

二、对象原型__proto__

同样地,每一个对象都会有一个属性 __proto__(注意这里两边都是两个下划线),指向其构造函数的prototype原型对象,因此称为对象原型。之所以我们对象可以使用构造函数prototype 原型对象的属性和方法,就是因为对象有__proto__原型的存在。

  • __proto__对象原型和原型对象 prototype 是等价的
  • 实际开发中,不可以使用__proto__属性,它只是内部指向原型对象 prototype

三、构造函数constructor

对象原型(proto) 和构造函数(prototype)原型对象 里面都有一个 constructor属性,我们称为构造函数。它指回构造函数本身,就是说指向把这个对象创建出来的那个构造函数,从这个属性可以看出这个对象是哪个构造函数创建出来的。

构造函数、原型对象、实例对象之间的关系:

在这里插入图片描述
实例对象的constructor是通过__proto__指向原型对象,原型对象再指回构造函数。

四、原型链

原型对象prototype的本质是一个对象,所以它也应该有一个__proto__属性,它的__proto__指向一个原型对象prototype,我们暂且称它为A,查看这个原型对象A的constructor,发现它指向的是Object构造函数,也就是说某一个构造函数的原型对象prototype的__proto__指向的是Object类的原型对象,自然这个Object类的原型对象指向Object构造函数。
继续往上查找,Object类的原型对象prototype也会有一个__proto__属性指向一个原型对象,但是因为Object以及是我们js中对象的最顶层的类了,所以就指向空了。
这个过程我们可以把它画出来,可以看到形成了一条链,从实例对象的对象原型开始一直向上溯源对象原型,形成了一条原型链
在这里插入图片描述
我们对象可以使用构造函数prototype原型对象的方法,就是因为对象有__proto__原型的存在。原型链为对象的查找机制提供一个方向,或者说一条路线。

原型链查找机制:

  1. 当访问一个实例对象的属性(包括方法)时,首先查找这个实例对象自身有没有该属性;
  2. 如果没有就查找它的原型(也就是__proto__指向的prototype原型对象)上是否有这个方法;
  3. 如果还没有就查找原型对象的原型(Object的原型对象)上是否有这个方法;
  4. 依次类推一直找到null还没有,就说明没有这个属性或者方法。
  5. 如果每一步都能查找这个方法,那么遵循就近原则,采用的是最近的那一步的属性或者方法。比如实例对象自身有该属性,属性值为1,原型对象上也有该属性,属性值为2,最后这个属性值返回的是1。

其实有点神似dom元素的冒泡机制,可以用这个帮助理解。

最后,es6中的类的本质其实就是构造函数,所以构造函数的这些原型对象、对象原型、constructor构造函数、原型链在es6中的class中都是存在的,而且原理一样。而class的写法更简洁,在使用类的继承时也比较简洁,结构也更加清晰,更符合面向对象的编程方法,因此class是一种语法糖。

猜你喜欢

转载自blog.csdn.net/weixin_43790653/article/details/123624944