跳转到内容

原型到原型链

前言

JavaScript 是动态的,本身不提供一个 class 的实现。即便是在 ES2015/ES6 中引入了 class 关键字,但那也只是语法糖,JavaScript 仍然是基于原型的。 当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象(object)都有一个私有属性(称之为 __proto__)指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(__proto__),层层向上直到一个对象的原型对象为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。 几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例。

谈谈你对原型的理解?

在 JavaScript 中,每个函数对象都有一个prototype 属性,这个属性指向函数的原型对象。使用原型对象的好处是所有对象实例共享它所包含的属性和方法。

什么是原型链?原型链解决的是什么问题?

原型链解决的主要是继承问题。 每个对象拥有一个原型对象,通过 __proto__ (读音: dunder proto,double underscore proto) 指向其原型对象,并从中继承方法和属性,同时原型对象也可能拥有原型,这样一层一层,最终指向null(Object.prototype.__proto__ 指向的是null)。这种关系被称为原型链,通过原型链,一个对象可以拥有定义在其他对象中的属性和方法。 构造函数 Parent、Parent.prototype 和 实例 p 的关系如下:(p.__proto__ === Parent.prototype)__proto__ (读音: dunder proto,dunder是“double underscore”的简拼)

double underscore proto 指 __proto__ 翻译做隐式原型链,为什么叫隐式呢?一般双下划线开头并结尾的属性和方法是不希望被直接使用和操作的,这种属性我们通常称它为隐式。

prototype 和 __proto__ 区别是什么?

  • prototype 是构造函数的属性。
  • __proto__ 是每个实例都有的属性,指向其原型对象。
  • 实例的 __proto__ 与其构造函数的 prototype 指向的是同一个对象。

解析原型链

实例对象和构造函数都可以指向原型:

javascript
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype); //true

在这里插入图片描述 既然实例对象和构造函数都可以指向原型,那么原型是否有属性指向构造函数或者实例呢?

constructor

指向实例倒是没有,因为一个构造函数可以生成多个实例,但是原型指向构造函数倒是有的,这就要讲到第三个属性:constructor,每个原型都有一个constructor属性指向关联的构造函数

javascript
function Person() {
}
console.log(Person === Person.prototype.constructor); //true

所以再更新下关系图: 在这里插入图片描述 综上我们已经得出:

javascript
function Person() {
}
var person = new Person();
console.log(person.__proto__ == Person.prototype) // true
console.log(Person.prototype.constructor == Person) // true
// 顺便学习一个ES5的方法,可以获得对象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) //true

原型对象也可能拥有原型,这样一层一层,最终指向null(Object.prototype.__proto__ 指向的是null)。 最终关系图: 在这里插入图片描述

参考