JS原型链详解

原型链是 JavaScript 实现继承的核心机制,本质是一条「实例与原型之间的引用链条」,用于解决属性和方法的查找、共享与继承问题,理解原型链是掌握 JavaScript 面向对象编程的关键。

一、先搞懂 3 个核心概念(原型链的基础)

在讲原型链之前,必须先明确 prototype__proto__constructor 这三个不可分割的概念,它们是构成原型链的基本单元。

1. prototype(原型属性 / 显式原型)

  • 定义 :只有函数(构造函数)才拥有 prototype 属性,它指向一个对象(称为「原型对象」),这个对象的作用是存放所有实例需要共享的属性和方法

  • 通俗理解:构造函数的「原型仓库」,所有通过该构造函数创建的实例,都能共享这个仓库里的内容,避免方法重复创建浪费内存。

  • 示例

    js 复制代码
    // 构造函数
    function Person(name) {
      this.name = name; // 实例私有属性
    }
    // prototype 指向原型对象,存放共享方法
    Person.prototype.sayName = function() {
      console.log('我的名字:', this.name);
    };
    
    console.log(Person.prototype); // { sayName: ƒ, constructor: ƒ Person() }

2. __proto__(原型链指针 / 隐式原型)

  • 定义 :几乎所有对象(除 null/undefined都拥有 __proto__ 属性(ES6 规范中称为 [[Prototype]]__proto__ 是浏览器提供的访问接口),它指向 创建该对象的构造函数的原型对象(prototype

  • 通俗理解:对象的「原型导航器」,通过它可以找到自己的 "原型仓库",进而向上查找属性 / 方法。

  • 示例

    js 复制代码
    const person1 = new Person('张三');
    // person1 的 __proto__ 指向 Person.prototype
    console.log(person1.__proto__ === Person.prototype); // true
    console.log(person1.__proto__.sayName === Person.prototype.sayName); // true

3. constructor(构造函数指向)

  • 定义 :原型对象(prototype)中默认包含 constructor 属性,它指向对应的构造函数本身,用于标识对象的创建来源。

  • 作用:修复原型指向后,保证实例能正确追溯到构造函数(避免继承时构造函数指向混乱)。

  • 示例

    js 复制代码
    // 原型对象的 constructor 指向构造函数
    console.log(Person.prototype.constructor === Person); // true
    // 实例可通过 __proto__ 找到 constructor
    console.log(person1.__proto__.constructor === Person); // true
    console.log(person1.constructor === Person); // true(自动向上查找)

二、原型链的核心定义与形成过程

1. 核心定义

原型链是由 __proto__ 串联起来的「对象 → 原型对象 → 上层原型对象 → ... → null」的链式结构,当访问一个对象的属性 / 方法时,JavaScript 会先在对象自身查找,找不到则通过 __proto__ 向上查找原型对象,依次类推,直到找到属性 / 方法或到达原型链末端(null)。

2. 原型链的形成过程(三步成型)

我们以 Person 实例为例,拆解原型链的形成:

  1. 第一步 :创建构造函数 Person,其 prototype 指向 Person 原型对象(包含 sayName 方法和 constructor);
  2. 第二步 :通过 new Person() 创建实例 person1person1.__proto__ 指向 Person.prototype(形成第一层链接);
  3. 第三步Person.prototype 是一个普通对象,它的 __proto__ 指向 Object.prototype(JavaScript 所有对象的根原型),Object.prototype.__proto__ 指向 null(原型链末端)。

最终形成的原型链:

plaintext

js 复制代码
person1(实例)
  ↓ __proto__
Person.prototype(Person 原型对象)
  ↓ __proto__
Object.prototype(根原型对象)
  ↓ __proto__
null(原型链末端)

可视化示例

js 复制代码
// 验证原型链结构
const person1 = new Person('张三');

// 第一层:person1 -> Person.prototype
console.log(person1.__proto__ === Person.prototype); // true
// 第二层:Person.prototype -> Object.prototype
console.log(Person.prototype.__proto__ === Object.prototype); // true
// 第三层:Object.prototype -> null
console.log(Object.prototype.__proto__ === null); // true
// 完整原型链:person1 -> Person.prototype -> Object.prototype -> null

三、原型链的核心作用:属性 / 方法查找机制

这是原型链最核心的功能,遵循「自身优先,向上追溯,末端终止」的规则:

1. 查找规则步骤

  1. 当访问对象的某个属性 / 方法时,先在对象自身 的属性中查找(比如 person1.name,直接在 person1 上找到);
  2. 如果自身没有,就通过 __proto__ 向上查找原型对象 (比如 person1.sayName(),自身没有,找到 Person.prototype 上的 sayName);
  3. 如果原型对象也没有,继续通过原型对象的 __proto__ 向上查找上层原型 (比如 person1.toString()Person.prototype 没有,找到 Object.prototype 上的 toString);
  4. 直到找到目标属性 / 方法,或到达原型链末端 null,此时返回 undefined(属性)或报错(方法)。

2. 代码示例

js 复制代码
const person1 = new Person('张三');

// 1. 查找自身属性:name
console.log(person1.name); // 张三(自身存在,直接返回)

// 2. 查找原型方法:sayName
console.log(person1.sayName()); // 我的名字:张三(自身没有,向上找到 Person.prototype)

// 3. 查找上层原型方法:toString
console.log(person1.toString()); // [object Object](Person.prototype 没有,向上找到 Object.prototype)

// 4. 查找不存在的属性:age
console.log(person1.age); // undefined(原型链末端仍未找到,返回 undefined)

3. 注意:属性修改仅影响自身,不影响原型

原型链是「只读」的查找链路,修改对象的属性时,只会修改对象自身,不会改变原型对象的属性(除非直接显式修改原型):

js 复制代码
// 错误:试图修改原型方法(实际是给 person1 新增了一个私有方法 sayName,覆盖了原型查找)
person1.sayName = function() {
  console.log('我是私有方法:', this.name);
};
person1.sayName(); // 我是私有方法:张三(优先访问自身方法)
console.log(Person.prototype.sayName()); // 我的名字:undefined(原型方法未被修改)

四、原型链与继承的关系

原型链是 JavaScript 继承的底层支撑 ,所有继承方式(原型链继承、组合继承等)本质都是通过修改 __proto__prototype,构建新的原型链结构,实现子类对父类属性 / 方法的继承。

示例:简单继承的原型链结构

js 复制代码
// 父类构造函数
function Animal(name) {
  this.name = name;
}
Animal.prototype.sayName = function() {
  console.log('名称:', this.name);
};

// 子类构造函数
function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}
// 构建继承:让 Dog.prototype.__proto__ 指向 Animal.prototype
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

// 子类实例的原型链
const dog1 = new Dog('旺财', '中华田园犬');
// 原型链:dog1 -> Dog.prototype -> Animal.prototype -> Object.prototype -> null
console.log(dog1.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
// 继承生效:dog1 能访问 Animal.prototype 的 sayName 方法
dog1.sayName(); // 名称:旺财

五、原型链的末端:Object.prototypenull

  1. Object.prototype :是 JavaScript 所有对象的「根原型」,所有对象最终都会继承它的属性和方法(如 toString()hasOwnProperty()valueOf() 等);

  2. null :是原型链的「终点」,Object.prototype.__proto__ 指向 null,表示没有上层原型,查找过程到此终止;

  3. 验证

    js 复制代码
    console.log(Object.prototype.__proto__); // null
    console.log(Object.prototype.hasOwnProperty('toString')); // true(根原型的自有方法)
    console.log(person1.hasOwnProperty('name')); // true(自身属性)
    console.log(person1.hasOwnProperty('sayName')); // false(原型上的方法,非自身属性)

六、常见误区

  1. 混淆 prototype__proto__prototype 是函数的属性,__proto__ 是对象的属性,两者的关联是「对象.proto = 构造函数.prototype」;
  2. 原型链是可写的__proto__ 可以手动修改(不推荐,会破坏原有继承结构,影响性能);
  3. 所有对象都有 prototype :只有函数才有 prototype,普通对象只有 __proto__
  4. hasOwnProperty 能查找原型属性hasOwnProperty 仅判断对象自身是否有该属性,不会向上查找原型链。

总结

  1. 原型链的核心是 __proto__ 串联的链式结构,末端是 null,根节点是 Object.prototype
  2. 3 个核心概念:prototype(函数的原型仓库)、__proto__(对象的原型指针)、constructor(原型的构造函数指向);
  3. 核心功能:实现属性 / 方法的分层查找(自身 → 原型 → 上层原型 → ... → null),支撑 JavaScript 继承机制;
  4. 本质:通过共享原型对象的属性 / 方法,实现代码复用,减少内存消耗。
相关推荐
风止何安啊4 小时前
Steam玩累了?那用 Node.js 写个小游戏:手把手玩懂 JS 运行环境
前端·javascript·node.js
simon91244 小时前
ElementUI:表格如何展示超出单元格的内容且不影响单元格?
前端·vue.js·element
天外天-亮4 小时前
Vue 中常用的指令
前端·javascript·vue.js
清风细雨_林木木4 小时前
vite与vue的cli的区别
前端·javascript·vue.js
亚洲小炫风4 小时前
react 资源清单
前端·javascript·react.js
努力学算法的蒟蒻4 小时前
day42(12.23)——leetcode面试经典150
算法·leetcode·面试
IT古董4 小时前
【前端】Headless UI 深度实战:构建可访问、可定制的现代前端组件
前端·ui
南囝coding4 小时前
Knip - 一键清理项目无用代码
前端·后端
五点六六六5 小时前
跨端RN 与 浏览器Web 的 长渲染性能 差异 与 底层 揭秘
前端·react native·webgl