原型

概念

  • 原型就是一个概念
  • 每个函数都有一个prototype属性,该属性指向当前函数的显示原型对象,指向Object对象
  • 每个实例对象都有一个__proto__属性,该属性指向当前实例对象的隐示原型对象,指向Object对象
  • 构造函数的显示对象 === 当前构造函数的实例对象的隐示原型对象

把一个构造函数可以看成一个类,原型是所有类都有的一个属性,原型的作用就是给这个类的每一个对象都添加一个统一的方法。
在es5时候我们通常使用大写开始的函数作为构造函数。

  1. // 创建一个原型对象
  2. function Person() { }
  3. //1、 每个函数都有一个Prototype
  4. console.log(Person.prototype); // {constructor: ƒ} 空对象 ==>原型对象Object对象(显示对象)、
  5. console.log(Person.prototype.constructor); //指向原型对象本身
  6. // 声明当前的构造函数
  7. console.log(Person.prototype.constructor); // 指向函数本身
  8. // 创建一个实例
  9. let person = new Person()
  10. // 2、 每个实例对象都有一个__proto__属性,这个属性指向当前对象的原型对象(隐式对象)
  11. // 3、构造函数的显示对象===实例的隐式对象
  12. console.log(Person.prototype === person.__proto__); // true

显示原型vs隐式原型

每个函数都有一个prototype显示原型。而每个实例都有__proto__,称为隐示原型。对象的隐示对象的值为构造函数显示对象的值。Person.prototype === person.__proto__

  • 函数的prototype属性:在定义函数时自动添加,默认为空Object对象
  • 对象的__proto__属性:创建时自动添加,默认为构造函数的prototype属性值
  • 我们能直接操作显示对象而不能直接操作隐示对象

问:为什么prototype创建出来的叫显示原型,而__proto__叫隐示原型?而不能直接操作隐示原型呢?

  1. // 创建一个构造函数
  2. function Person(){}
  3. //问 为什么不是使用__proto__ 而是使用prototype呢
  4. Person.prototype.eat = function(){
  5. return '吃饭'
  6. }
  7. let person = new Person()
  8. person.__proto__.eat = function(){
  9. return '吃饭'
  10. }

对象的属性中,不加的属性/方法是内置对象意味着我们可以直接去调用,而加开头的我们是不可以直接使用的。所以__proto__是不可以直接使用的,所以才叫做隐示

什么时候用到原型

但需要操作公共方法的时候可以直接放在原型对象上,供所有的实例对象使用,可以节省内存开销。

原型链

概念

  • 查找对象属性的时候先在自身查找,如果自身没有该属性,会沿着__proto__去原型对象查找,如果没有回返回undefined
  • 找到Object的原型。如果没有返回undefined,如果调用的是函数,会返回 is not function
  • __proto__这条线就叫原型链,原型链的顶层是null

    原型链示意图

    原型vs原型链 - 图1

    实例对象vs函数对象

  • 函数对象:可调用的对象,函数对象既可以点属性,又可以调用函数。

  • 实例对象:所有的原型对象都是实例对象

image-20200425162657554.png