基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

JavaScript 中的原型(Prototype)和原型链(Prototype Chain)

知识点图片

在 JavaScript 中,原型(Prototype)原型链(Prototype Chain)是理解 JavaScript 面向对象编程(OOP)和继承机制的绝对核心。

这两个概念听起来抽象,但如果用大白话来解释,其实就是一个“共享资源库”“顺藤摸瓜找资源的过程”

下面为你详细拆解这两个概念:


一、 什么是原型(Prototype)?

在 JavaScript 中,几乎所有的对象都有一个与之关联的另一个对象,这个关联的对象就叫做“原型”。对象会从原型继承属性和方法。

我们可以把原型理解为一个“模板”“共享仓库”

为了搞懂原型,必须先区分两个极易混淆的属性:prototype__proto__

1. prototype(显式原型)

  • 谁拥有它? 只有函数(Function)才拥有 prototype 属性(箭头函数除外)。
  • 它是干嘛的? 当你把一个函数当作构造函数(Constructor)来创建实例时,这个 prototype 属性就是即将创建出来的实例的原型
  • 比喻: prototype 就像是汽车工厂里的设计图纸

2. __proto__(隐式原型 / [[Prototype]]

  • 谁拥有它? 所有对象(包括函数、数组、普通对象等)都拥有 __proto__ 属性(现在官方推荐使用 Object.getPrototypeOf() 来获取)。
  • 它是干嘛的? 它指向创建这个对象的构造函数的 prototype(即它的原型)。
  • 比喻: __proto__ 就像是出厂的汽车上自带的一本说明书,上面写着:“我是根据哪张图纸(prototype)造出来的”。

代码验证:

javascript
// 1. 创建一个构造函数
function Person(name) {
  this.name = name;
}

// 2. 在函数的 prototype 上添加方法(放入共享仓库)
Person.prototype.sayHello = function() {
  console.log("Hello, my name is " + this.name);
};

// 3. 实例化对象
let p1 = new Person("Alice");

// 4. 验证他们的关系!
console.log(p1.__proto__ === Person.prototype); // true (p1的说明书指向了Person的图纸)

二、 什么是原型链(Prototype Chain)?

当试图访问一个对象的属性或方法时,JavaScript 引擎会执行一个查找过程

查找规则(原型链机制):

  1. 首先在对象自身查找该属性。
  2. 如果自身没有,就顺着 __proto__ 去它的原型对象上找。
  3. 如果原型对象上也没有,就继续顺着原型对象的 __proto__上一级原型对象上找。
  4. 这样一层一层向上找,就像一条链子,这就是原型链
  5. 直到找到顶端 Object.prototype,如果它的 __proto__ 也就是 null 也没有,就返回 undefined

原型链的完整示例:

javascript
function Person(name) {
    this.name = name;
}
Person.prototype.sayHello = function() {}

let p1 = new Person("Bob");

// 1. 访问 p1.name
// p1 自身有 name 属性,直接返回 "Bob"

// 2. 访问 p1.sayHello()
// p1 自身没有 sayHello,顺着 p1.__proto__ 找到 Person.prototype,找到了,执行!

// 3. 访问 p1.toString()
// p1 自身没有 -> 去 Person.prototype 找,也没有
// -> 顺着 Person.prototype.__proto__ 找,找到了 Object.prototype
// Object.prototype 上有 toString() 方法,执行!

// 4. 访问 p1.fly()
// p1 -> Person.prototype -> Object.prototype -> null 
// 全部找完都没有,抛出错误或返回 undefined。

原型链的终点:

所有普通对象的原型链最终都会指向 Object.prototype,而 Object.prototype.__proto__ === null
null 就是原型链的尽头。


三、 牢记“核心铁三角”

如果你能看懂下面这三层关系,你就彻底掌握了原型:

  1. 实例.__proto__ === 构造函数.prototype
    (实例的隐式原型指向构造函数的显式原型)
  2. 构造函数.prototype.constructor === 构造函数
    (原型对象里面有一个 constructor 属性,指回构造函数本身)
  3. Object.getPrototypeOf(实例) === 构造函数.prototype
    (这是 ES6 推荐的获取原型的方法,等同于 __proto__)

四、 常见数据类型的原型链图解

了解原生对象是如何通过原型链连接的,对调试非常有帮助:

  • 普通对象: let obj = {}
    obj -> Object.prototype -> null

  • 数组: let arr = []
    arr -> Array.prototype -> Object.prototype -> null
    (这就是为什么数组能用 pushmap 等方法,因为它们在 Array.prototype 上)

  • 函数: function fn() {}
    fn -> Function.prototype -> Object.prototype -> null
    (这就是为什么函数能用 callapplybind)


五、 相关重要方法

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

    javascript
    p1 instanceof Person; // true
    p1 instanceof Object; // true (因为 Object.prototype 在 p1 的原型链上)
  2. hasOwnProperty()
    判断一个属性是存在于对象自身上,还是存在于原型链上。

    javascript
    p1.hasOwnProperty('name'); // true (自身属性)
    p1.hasOwnProperty('sayHello'); // false (来自原型链)
  3. Object.create()
    创建一个新对象,使用现有的对象来提供新创建的对象的 __proto__

    javascript
    let parent = { house: "别墅" };
    let child = Object.create(parent); // child.__proto__ === parent
    console.log(child.house); // "别墅"

总结

  • 原型是为了实现属性和方法的共享,节省内存。
  • 原型链是 JavaScript 实现继承的底层机制。当你调用对象的方法时,JS 引擎就是靠着原型链“顺藤摸瓜”找到该方法的。
  • ES6 的 class 语法(classextends)本质上只是原型继承的语法糖,底层依然是基于原型链在运作。
00:00
00:00