理清JavaScript原型链

72 阅读3分钟

工作以来,一直对原型链的识别较为模糊,没有系统的整理过,所以总觉得认识不够清晰,好记性不如烂笔头,在此整理一下,如果发现有什么错漏之处,还请各位朋友帮忙指出

1. 原型链的结构

原型链,顾名思义,是一个链路,主要作用是让我们在创建对象实例时,能继承一些公共类的属性和方法,所以原型链的入口是一个对象实例。 原型链的一个最小组成单元是以下三句话:

  1. 每个对象实例,都有一个原型方法,通过new 原型方法()创建了对象实例。
  2. 每个原型方法都有一个原型对象,并且可以通过原型方法.prototype访问到他的原型对象,原型对象也可以通过原型对象.constructor访问到原型方法。
  3. 每个对象实例也有一个属性,能访问到他的原型方法的原型对象,即:__proto__ 整个原型链的链路示意图如下,这条从左到右的绿线就是原型链的查找路径

Pasted image 20250913125007.png

2. 如果要手动搭建原型链,该怎么操作?

/**
 * 要构建原型链,首先要有原型对象
 */

const Person = function () {
}

// 准备了Person的原型对象 Person.prototype
Person.prototype.walkStyle = "erect"

// 准备了普通对象creature
const creature = {
    getPowerType: "food"
}

  

// 我们要创建的实例的构造函数
const Student = function (name) {
    this.name = name
}

// 根据构造函数构件原型链
Student.prototype = Person.prototype

Person.prototype.__proto__ = creature;
// 更推荐Object.setPrototypeOf(Person.prototype,creature)

const john = new Student("john");
console.log("john.walkStyle",john.walkStyle)
console.log("john.getPowerType",john.getPowerType)

以上代码只是为了示意原型链上的原型对象,就是一个普通的对象,任何对象都可以作为原型链上的对象而存在(严格来说原型对象要有constructor属性,但没有也能用),但我们一般不会用普通对象作为原型对象,据官方说这样做运行成本会较大,我们在平时的开发中,更多采用以下两种。

// 1. 函数的方式
// 创建原型对象
const Creature = function() {
}
Creature.prototype.getPowerType = 'food'

const Person = function() {
}
Person.prototype.walkStyle = 'erect'

// 实例对象的构造函数
const Student = function (name) {
    this.name = name
}
Student.prototype = Person.prototype
Person.prototype.__proto__ = Creature.prototype

const john = new Student("john")
console.log("john.walkStyle",john.walkStyle)
console.log("john.getPowerType",john.getPowerType)

// 2. class的方式
class Creature {
	constructor() {
		this.getPowerType = 'food'
	}
}
class Person extends Creature {
	constructor() {
		super()
		this.walkStyle = 'erect'
	}
}
class Student extends Person {
	constructor(name) {
		super()
		this.name = name
	}
}
const john = new Student('john');

console.log("john.walkStyle",john.walkStyle)
console.log("john.getPowerType",john.getPowerType)
// 是不是简洁了许多

3. 原型对象来自哪里?class的继承,如何对应到原型链上?

看完2中原型链的创建,不知道大家会不会有一个疑问,原型对象是哪里来的,啥时候建的?2.2中的继承,对应到我们说的原型对象上,是咋对应的呢?
一个一个分析:
1.原型对象是啥时候建的?
一句话,在声明构造函数的时候,他的原型,便已经确定了,这是js引擎帮助我们做的事。
2. class的继承,是怎么做的?
这个问题,只要弄懂了class的结构,就不是问题了。

需要的前置知识:
1. class是一个创建类的语法糖,使用typeof class Student{} 检测一下,发现返回的是function,这说明他也是一个函数。
2. class一旦有继承,就必须在constructor中写super(),如果没有constructor,他就会自动调用super()

class内部可以简单分为两大块,一个是constructor内部,一个是constructor外部,constructor内部,就是构造函数中的内容,constructor外部,就是构造函数的原型中的内容。
所以,new一个class的阶段,也是有三个组成元素(还记得最开始的三句”每个“吗),实例对象、构造函数、构造函数的原型对象。
而extends继承,可以分为两个阶段:
第一个阶段:定义时,父类会将自身的原型对象赋值给子类的原型对象的__proto__属性,组装在原型链上
第二个阶段:运行时,在new时,子类会调用super(),父类会执行父类的constructor函数,给实例对象赋值。
所以说class是一个语法糖,跟我们用函数实现继承的基本相同的,只是会更加简洁一些。