09.函数的原型 prototype

74 阅读3分钟

一、认识对象的原型

  • JavaScript当中每个对象都有一个特殊的内置属性 [[prototype]],这个特殊的对象可以指向另外一个对象。
  • 那么这个对象有什么用呢?
    • 当我们通过引用对象的属性key来获取一个value时,它会触发 [[Get]]的操作;
    • 这个操作会首先检查该属性是否有对应的属性,如果有的话就使用它;
    • 如果对象中没有该属性,那么会访问对象[[prototype]]内置属性指向的对象上的属性;
  • 那么如果通过字面量直接创建一个对象,这个对象也会有这样的属性吗?如果有,应该如何获取这个属性呢?
    • 答案是有的,只要是对象都会有这样的一个内置属性;
  • 获取的方式有两种:
    • 方式一:通过对象的 __proto__ 属性可以获取到(但是这个是早期浏览器自己添加的,存在一定的兼容性问题);
    • 方式二:通过 Object.getPrototypeOf 方法可以获取到;

二、函数的原型 prototype

  • 那么我们知道上面的东西对于我们的构造函数创建对象来说有什么用呢?

    • 它的意义是非常重大的,接下来我们继续来探讨;
  • 这里我们又要引入一个新的概念:所有的函数都有一个prototype的属性:

    图片.png

  • 是不是因为函数是一个对象,所以它有prototype的属性呢?

    • 不是的,因为它是一个函数,才有了这个特殊的属性;
    • 而不是它是一个对象,所以有这个特殊的属性;

    图片.png

三、new操作符

  • 我们前面讲过new关键字的步骤如下:

    1. 在内存中创建一个新的对象(空对象);
    2. 这个对象内部的[[prototype]]属性会被赋值为该构造函数的prototype属性;(后面详细讲);
  • 那么也就意味着我们通过Person构造函数创建出来的所有对象的[[prototype]]属性都指向Person.prototype:

    图片.png

四、创建对象的内存表现

图片.png

图片.png

图片.png

五、赋值为新的对象

图片.png

六、prototype添加属性

图片.png

七、constructor属性

  • 事实上原型对象上面是有一个属性的:constructor

  • 默认情况下原型上都会添加一个属性叫做constructor,这个constructor指向当前的函数对象;

    图片.png

八、重写原型对象

  • 如果我们需要在原型上添加过多的属性,通常我们会重新整个原型对象:

    图片.png

  • 前面我们说过, 每创建一个函数, 就会同时创建它的prototype对象, 这个对象也会自动获取constructor属性;

    • 而我们这里相当于给prototype重新赋值了一个对象, 那么这个新对象的constructor属性, 会指向Object构造函数, 而不是Person构造函数了

九、原型对象的constructor

  • 如果希望constructor指向Person,那么可以手动添加:
  • 上面的方式虽然可以, 但是也会造成constructor的[[Enumerable]]特性被设置了true.
    • 默认情况下, 原生的constructor属性是不可枚举的.
    • 如果希望解决这个问题, 就可以使用我们前面介绍的Object.defineProperty()函数了

图片.png

十、创建对象 – 构造函数和原型组合

  • 我们在上一个构造函数的方式创建对象时,有一个弊端:会创建出重复的函数,比如running、eating这些函数
  • 那么有没有办法让所有的对象去共享这些函数呢?
    • 将这些函数放到Person.prototype的对象上即可;

图片.png