JavaScript原型和原型链联系和区别

135 阅读6分钟

原型和原型链

在 JavaScript 中,原型(Prototype)和原型链(Prototype Chain)是实现继承和对象属性访问的重要机制,以下是它们的详细介绍、联系与区别:

原型(Prototype)

  • 定义:在 JavaScript 里,每个函数都有一个 prototype 属性,它是一个对象,包含了可以被该函数创建的实例共享的属性和方法。当使用构造函数创建实例时,实例的 __proto__ 属性(非标准,但被广泛支持,标准方式是通过 Object.getPrototypeOf() 获取 )会指向构造函数的 prototype
  • 示例
function Person(name) {
    this.name = name;
}
Person.prototype.sayHello = function() {
    console.log(`Hello, my name is ${this.name}`);
};
const alice = new Person('Alice');
console.log(alice.__proto__ === Person.prototype); // true
alice.sayHello(); // 输出:Hello, my name is Alice

在这个例子中,Person 是构造函数,Person.prototype 是它的原型对象,包含了 sayHello 方法。alice 是 Person 的实例,通过原型链接,alice 可以访问 Person.prototype 上的 sayHello 方法。

  • 每一个函数都有一个特殊的属性叫做原型prototype
function doSomething(){}
console.log(doSomething.prototype)

控制台输出结果

image.png

上面喝这个对象,就是常说的原型对象

原型链(Prototype Chain)

  • 定义:原型链是由多个对象的原型连接起来形成的链条。当访问一个对象的属性或方法时,JavaScript 引擎首先在对象自身查找,如果找不到,就会沿着对象的 __proto__ (原型)属性,去它的原型对象中查找。如果原型对象中也没有,就继续沿着原型对象的 __proto__ 向上查找,一直到 Object.prototype ,如果 Object.prototype 中还没有,就返回 undefined 。
  • 示例
function Animal() {}
Animal.prototype.speak = function() {
    console.log('I am an animal');
};

function Dog() {}
Dog.prototype = Object.create(Animal.prototype); // Dog 的原型继承自 Animal 的原型
Dog.prototype.bark = function() {
    console.log('I am barking');
};

const myDog = new Dog();
myDog.speak(); // 输出:I am an animal,通过原型链找到 Animal.prototype 上的 speak 方法
myDog.bark(); // 输出:I am barking,直接访问 Dog.prototype 上的 bark 方法

在这个例子中,myDog 的原型是 Dog.prototypeDog.prototype 的原型是 Animal.prototypeAnimal.prototype 的原型是 Object.prototype,这样就形成了一条原型链。

联系

  • 原型是原型链的基础组成部分,原型链是由多个原型对象串联起来的结构。对象通过原型链,实现了对其原型以及原型的原型等上级原型对象中属性和方法的继承和访问。
  • 原型链的查找机制依赖于对象的原型关系,正是因为对象的 __proto__ 指向其原型,才使得属性和方法查找可以沿着原型链逐级进行。

区别

  • 概念侧重:原型强调的是单个对象与其可共享属性和方法集合(即原型对象)的关系;而原型链更侧重于描述多个对象之间通过原型连接形成的层级关系,以及属性和方法的查找路径。
  • 表现形式:原型通常是一个具体的对象,比如函数的 prototype 属性所指向的对象;原型链则是一个逻辑上的链条概念,是由多个原型对象链接起来的抽象结构。
  • 作用体现:原型主要用于为构造函数创建的实例提供共享的属性和方法,实现代码复用;原型链除了支持继承,还决定了属性和方法查找的顺序和范围,影响着对象能否访问到特定的属性和方法。

总之,原型和原型链是 JavaScript 中紧密相关且非常重要的概念,理解它们有助于深入掌握 JavaScript 的继承机制和对象属性访问原理。

__proto__是什么

在 JavaScript 中,__proto__ 是一个非标准但广泛支持的对象属性,用于访问或操作对象的原型(Prototype) 。它是 JavaScript 原型继承机制的核心概念之一,以下是详细解析:

1. 什么是 __proto__

  • 作用__proto__ 是对象的一个内置属性,指向该对象的原型对象(Prototype Object)
  • 本质:它是 JavaScript 引擎实现 [[Prototype]] 内部属性的浏览器兼容层(ES6 前无标准访问方式,ES6 后推荐用 Object.getPrototypeOf() 替代)。
  • 关系:每个对象(除 Object.prototype 外)都有 __proto__,形成原型链(Prototype Chain)

2. 原型链与继承

JavaScript 通过原型链实现继承:

  • 原型链继承是 JavaScript 中实现继承的一种核心机制,其核心思想是通过原型对象建立对象之间的继承关系
  • 每个对象都有一个内部属性 [[Prototype]](可通过 __proto__ 访问,ES6 后推荐用 Object.getPrototypeOf()
  • 当访问一个对象的属性或方法时,JavaScript 引擎会先在对象本身查找,若找不到则沿着原型链__proto__ 向上查找,直到找到或到达原型链顶端 Object.prototype
  • 如果该属性在对象本身及其原型链中(包括 Object.prototype)都不存在,返回的结果**undefined**,
  • 原型链末端(即 Object.prototype.__proto__) 值为 null(但这是原型链的终点,而非属性查找结果)。
// 父对象(原型)
const animal = {
  type: "Animal",
  speak() { console.log("动物叫"); }
};

// 子对象,通过 __proto__ 继承 animal
const dog = {
  breed: "Dog",
  bark() { console.log("汪汪!"); }
};
dog.__proto__ = animal; // dog 的原型指向 animal

// 访问属性:自身有 breed,type 从原型继承
console.log(dog.breed); // "Dog"
console.log(dog.type);  // "Animal"

// 调用方法:自身有 bark,speak 从原型继承
dog.bark();   // "汪汪!"
dog.speak();  // "动物叫"

3. __proto__ vs prototype

概念__proto__prototype
所属对象所有对象(包括函数、数组等)函数对象(如构造函数)
作用指向对象的原型对象构造函数的实例原型(实例的 __proto__ 指向它)
示例const obj = {}; obj.__proto__function Person(){}; Person.prototype
function Person(name) {
  this.name = name;
}
// 构造函数 Person 的 prototype
Person.prototype.sayHi = function() {
  console.log("Hi, " + this.name);
};

// 实例化
const alice = new Person("Alice");

// alice 的 __proto__ 指向 Person.prototype
console.log(alice.__proto__ === Person.prototype); // true
alice.sayHi(); // "Hi, Alice"(沿原型链找到方法)

4. 为什么不推荐直接用 __proto__

虽然 __proto__ 方便,但存在以下问题:

  • 非标准:ES6 前未被官方规范,仅浏览器实现(Node.js 也支持,但属于历史遗留)。
  • 性能差:修改 __proto__ 会改变对象的原型链,导致后续属性查找变慢。
  • 风险高:随意修改原型可能破坏内置对象(如 ArrayObject)的默认行为。

5. 标准替代方案(ES6+)

ES6 提供了更安全、标准的 API 操作原型:

  • 获取原型Object.getPrototypeOf(obj)
  • 设置原型Object.setPrototypeOf(obj, proto)(替代 __proto__ = ...
const animal = { type: "Animal" };
const dog = { breed: "Dog" };

// 标准方式设置原型
Object.setPrototypeOf(dog, animal);
console.log(dog.type); // "Animal"

// 标准方式获取原型
console.log(Object.getPrototypeOf(dog) === animal); // true

6. 特殊情况:Object.prototype

Object.prototype 是所有对象的原型链顶端,它的 __proto__ 是 null(没有更高层原型):

console.log(Object.prototype.__proto__); // null

总结

  • __proto__ 是对象访问原型的非标准属性,用于实现原型链继承。
  • 现代开发中优先用 ES6 标准 APIObject.getPrototypeOf / Object.setPrototypeOf)操作原型,避免兼容性和性能问题。
  • 理解 __proto__ 有助于掌握 JavaScript 的原型继承机制,但实际代码中应谨慎直接使用。