理解原型

45 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

关键词:

构造函数 原型对象 实例 prototype [[prototype]] __proto__

构造函数

function Student() {
	this.name = 'Jane'
}

无论何时,只要创建一个函数,就会按照特定的规则为这个函数创建一个 prototype 属性(指向原型对象)。默认情况下,所有的原型对象自动获取一个名为 constructor 的属性,并指向与之相关的构造函数。

Student // 创建函数
    .prototype // 为函数创建一个 prototype 属性,指向原型
        .constructor // 默认情况下,原型对象自动获得一个 constructor 属性,指向与之关联的构造函数

image.png

在自定义构造函数时,原型对象默认只会获得 constructor 属性,其他的所有方法和属性都继承自 Object。

实例

使用自定义构造函数创建一个实例后,这个实例的内部 [[Prototype]] 指针会被赋值为构造函数的原型对象。

const student = new Student();

image.png

[[Prototype]] 属性是不能直接访问的,也没有访问 [[Prototype]] 属性的标准。

访问内部特性 [[prototype]] 的方式:

1. __proto__ 属性

不过现代浏览器( Firefox、Safari 和 Chrome )会在对象上暴露 __proto__ 属性,我们可以通过该属性访问原型。

student.__proto__

// 
student.__proto__ === Student.prototype // true

2. Object.getPrototypeOf() 方法

对于没有暴露 proto 属性的,可以使用 Object.getPrototypeOf() 方法获取内部特性 [[prototype]] 的值

Object.getPrototypeOf(student) === Student.prototype

构造函数和实例的关系

每个构造函数都有一个原型对象(prototype),原型有个属性(constructor)指回构造函数,实例内部有个指针(__proto__)指向原型。

构造函数、原型对象和实例是三个完全不同的对象

student !== Student
student !== Student.prototype
Student.prototype !== Student
  • 实例可以通过 proto 属性链接到原型对象
  • 构造函数可以通过 prototype 属性链接到原型对象
  • 构造函数和实例没有关系,但是构造函数的原型和实例有直接的联系
student.__proto__ === Student.prototype // true

// or
Student.isPrototypeOf(student); // true

使用同一个构造函数构建出来的两个实例共享同一个原型对象:

const stu1 = new Student();
const stu2 = new Student();

stu2.__proto__ === stu1.__proto__

image.png

**instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。**

object instanceof constructor

// object 某个实例对象
// constructor 某个构造函数

console.log(student instanceof Student) // true

// 所有自定义对象都继承自 Object
console.log(student instanceof Object) // true

原型的特点

1. 动态性

实例在修改原型之前已经存在,任何时候对原型对象所做的修改会反映到实例上。因为从原型上搜索值的过程是动态的。 注意,重写构造函数上的原型之后创建的实例才会引用新的原型,而在之前创建的实例仍然会引用最初的原型。

2. 共享特性

原型上所有的属性是实例间都是共享的

原型的问题

  1. 弱化了向构造函数传递初始参数的能力,会导致所有实例默认都取得相同的属性。
  2. 最主要的问题是原型的共享特性。原型上所有的属性是实例间都是共享的,如果属性是原始值还好,如果属性是引用类型,就会有问题了。