文章目录

JavaScript 原型链:原型继承与 Object.create()

发布于 2026-04-17 17:24:54 · 浏览 13 次 · 评论 0 条

JavaScript 原型链:原型继承与 Object.create()

JavaScript 中的对象并非孤立存在,它们之间通过一种名为“原型链”的机制相互连接。理解这一机制,是掌握 JavaScript 对象继承的核心。本指南将直接剖析原型链的查找逻辑,并演示如何使用 Object.create() 构建清晰的继承关系。

1. 理解原型对象的自动链接

在 JavaScript 中,绝大多数对象都有一个特殊的内部链接,指向另一个对象。这个被指向的对象被称为“原型”。当你试图访问一个对象的属性时,如果该对象本身没有这个属性,JavaScript 引擎就会自动去它的原型对象里查找。

  1. 打开浏览器的开发者工具(通常按 F12)。
  2. 切换到 Console(控制台)面板。
  3. 输入以下代码创建一个空对象并按回车:
    const myObj = {};
  4. 输入代码查看该对象的隐藏属性 __proto__(这是浏览器暴露出来的原型链接访问方式):
    myObj.__proto__;
  5. 观察返回的结果。你会发现它返回了一个对象,这个对象就是 JavaScript 内置的 Object.prototype。这意味着 myObj 继承了 Object.prototype 上预定义的方法,如 toString()hasOwnProperty()

2. 追踪原型链的查找路径

当访问一个不存在的属性时,引擎会沿着原型链一层层向上查找,直到找到该属性或到达链条顶端。如果到达顶端(通常是 null)仍未找到,则返回 undefined

下面的流程图展示了属性查找的完整过程:

graph TD A[当前对象 obj] -->|有属性?| B(直接返回属性值) A -->|无属性?| C[查找 obj.__proto__] C -->|有属性?| D(返回原型上的属性值) C -->|无属性?| E[查找原型的 __proto__] E -->|有属性?| F(返回上级属性值) E -->|无属性?| G[到达 null] G --> H(返回 undefined)

为了验证这条链条,我们可以构造一个三层结构。

  1. 定义一个父级对象 grandParent,包含一个属性 name
    const grandParent = {
      name: 'Grandpa',
      origin: 'Top Level'
    };
  2. 使用 Object.create() 创建一个 parent 对象,让其原型指向 grandParent
    const parent = Object.create(grandParent);
    parent.role = 'Father';
  3. 再次使用 Object.create() 创建一个 child 对象,让其原型指向 parent
    const child = Object.create(parent);
    child.role = 'Son';
  4. 尝试访问 child 身上不存在的属性:
    console.log(child.origin);

    结果:控制台输出了 'Top Level'。这说明 JavaScript 引擎按顺序在 child -> parent -> grandParent 这条链上进行了查找。


3. 使用 Object.create() 实现纯原型继承

传统的构造函数继承方式(配合 new 关键字)往往代码冗余且逻辑复杂。Object.create() 提供了一种更直接、更纯粹的方式来指定对象的原型。它的作用是:创建一个新对象,并使用现有对象作为新创建对象的原型

3.1 基础语法

const newObj = Object.create(proto);
  • proto:作为新对象原型的对象(可以是 null)。
  • newObj:创建出来的新空对象,其 __proto__ 指向 proto

3.2 实操:构建一个工具库

假设你需要构建一个包含基础数学计算的工具库,并希望基于它创建一个带有日志功能的扩展版本。

  1. 创建基础计算器对象 baseCalculator,包含加法和减法方法:

    const baseCalculator = {
      add: function(a, b) {
        return a + b;
      },
      sub: function(a, b) {
        return a - b;
      }
    };
  2. 使用 Object.create() 创建 loggingCalculator,使其继承 baseCalculator

    const loggingCalculator = Object.create(baseCalculator);
  3. 添加特有的日志方法到 loggingCalculator 上:

    loggingCalculator.log = function(result) {
      console.log('计算结果是:', result);
    };
  4. 调用继承来的方法验证功能:

    const sum = loggingCalculator.add(5, 3);
    loggingCalculator.log(sum);

    观察loggingCalculator 本身没有 add 方法,但它成功调用了原型上的 add


4. 属性遮蔽与 hasOwnProperty 判定

在原型链中,如果子对象和父原型拥有同名的属性,子对象的属性会“遮蔽”父对象的属性。这被称为属性遮蔽。

  1. 之前的 parent 对象上添加 name 属性:
    parent.name = 'Father';
  2. 重新检查 child.name 的值:
    console.log(child.name);

    结果:现在输出 'Father'。因为它在 parent 层级找到了 name,所以停止了向上查找 grandParent.name

为了区分一个属性是对象自身的,还是从原型继承的,使用 Object.prototype.hasOwnProperty() 方法。

  1. 测试 child 对象自身是否有 role 属性:
    console.log(child.hasOwnProperty('role')); // 输出: true
  2. 测试 child 对象自身是否有 name 属性:
    console.log(child.hasOwnProperty('name')); // 输出: false

5. 创建无原型的纯净对象

在某些特定场景下(如作为字典或 Map 使用),你希望对象不继承任何属性,甚至连 toString 都没有。此时可以将 null 传给 Object.create()

  1. 创建一个纯净对象 pureObj

    const pureObj = Object.create(null);
  2. 尝试访问toString 方法:

    console.log(pureObj.toString);

    结果:输出 undefined。因为它没有继承 Object.prototype

  3. 验证其原型链终点:

    console.log(Object.getPrototypeOf(pureObj)); // 输出: null

下表总结了不同创建方式的对象特性差异:

创建方式 原型指向 是否继承 Object.prototype 方法 适用场景
{}new Object() Object.prototype 普通数据对象
Object.create(proto) 指定的 proto 取决于 proto 自定义继承结构
Object.create(null) null 高性能字典、纯净数据存储

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文