对象原型与继承

js/prototype2026-10-29 11:03:47

原型(prototype)是 JavaScript 对象相互继承特性的机制。

继承和原型链

JavaScript 中所有的对象都有一个内置属性,称为它的原型。它本身是一个对象,故原型对象也会有它自己的原型,逐渐构成了原型链。原型链终止于拥有 null 作为其原型的对象上。

有个对象叫 Object.prototype,它是最基础的原型,所有对象默认都拥有它。Object.prototype 的原型是 null,所以它位于原型链的终点。

const myDate = new Date();

例如 myDate 对象的原型是 Date.prototype 对象,Date.prototype 的原型是 Object.prototype。

原型的作用

当你试图访问一个对象的属性时,如果在对象本身中找不到该属性,就会在原型中搜索该属性。如果仍然找不到该属性,那么就搜索原型的原型,以此类推,直到找到该属性,或者到达链的末端,在这种情况下,返回 undefined。

属性遮蔽

在对象的原型链中,当一个对象的属性与其原型链上某个对象的属性同名时,访问该属性时会优先访问对象自身的属性,而不是原型链上的属性。这种现象称为 属性遮蔽。

访问对象的原型

内部属性 [[Prototype]] 指向对象原型。访问对象原型的标准方法是 Object.getPrototypeOf()。实例属性 __proto__ 也指向对象原型,是一个非标准但广泛支持的方式来访问和设置对象的原型,但是不被推荐使用。

使用 __proto__ 是有争议且不被鼓励的。它的存在和确切行为仅作为遗留特性被标准化,以确保 Web 兼容性,但它存在一些安全问题和隐患。为了更好的支持,请优先使用 Object.getPrototypeOf() / Reflect.getPrototypeOf() 和 Object.setPrototypeOf() / Reflect.setPrototypeOf()。

设置新对象的原型

使用字面量

使用字面量创建的对象的原型是 Object.prototype。

可以通过 __proto__ 字面量属性将新创建对象的原型指向另一个对象。

该语法已经标准化,并且在实现中得到了优化,与 Object.prototype.__proto__ 相当不同。

const o = { a: 1 };
const p = { b: 2, __proto__: o };
// 原型链:p ---> o ---> Object.prototype ---> null

使用 Object.create()

Object.create() 方法创建一个新的对象,并允许你指定一个将被用作新对象原型的对象。

const personPrototype = {
  greet() {
    console.log("hello!");
  },
};

const carl = Object.create(personPrototype);
carl.greet(); // hello!

这里我们创建了一个 personPrototype 对象,它有一个 greet() 方法。然后我们使用 Object.create() 来创建一个以 personPrototype 为原型的新对象。现在我们可以在新对象上调用 greet(),而原型提供了它的实现。

使用构造函数

在 JavaScript 中,所有的函数都有一个名为 prototype 的属性。当你调用一个函数作为构造函数时,这个属性被设置为新构造对象的原型(按照惯例,在名为 __proto__ 的属性中)。

// Person 是一个函数
function Person(name) {
  this.name = name;
}

const personPrototype = {
  greet() {
    console.log(`你好,我的名字是 ${this.name}!`);
  },
};

// 为 Person 函数指定 prototype 属性
Object.assign(Person.prototype, personPrototype);
// 或者用下面的方法修改 Person 函数的 prototype 属性
Person.prototype.greet = personPrototype.greet;

在这段代码之后,使用 Person() 创建的对象将获得 Person.prototype 作为其原型,其中自动包含 greet 方法。

const p = new Person();
// p 的原型是 Person.prototype
// 由于 p 的原型有 greet() 方法,所以 p 也有 greet() 方法
p.greet();

使用类

class Polygon {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

class Square extends Polygon {
  constructor(sideLength) {
    super(sideLength, sideLength);
  }

  get area() {
    return this.height * this.width;
  }

  set sideLength(newLength) {
    this.height = newLength;
    this.width = newLength;
  }
}

const square = new Square(2);
// square ---> Square.prototype ---> Polygon.prototype ---> Object.prototype ---> null

修改现有对象的原型

Object.setPrototypeOf() 允许修改现有对象的 [[Prototype]] 内部属性。

const obj = { a: 1 };
const anotherObj = { b: 2 };
Object.setPrototypeOf(obj, anotherObj);
// obj ---> anotherObj ---> Object.prototype ---> null

自有属性

直接在对象中定义的而不是继承而来的属性,被称为 自有属性。

可以使用静态方法 Object.hasOwn() 检查一个属性是否是自有属性:

const irma = new Person("Irma");

console.log(Object.hasOwn(irma, "name")); // true
console.log(Object.hasOwn(irma, "greet")); // false

也可以在这里使用实例方法 Object.prototype.hasOwnProperty()。

const obj = {
  name: 'John',
  age: 30
};

console.log(obj.hasOwnProperty('name')); // 输出 true
console.log(obj.hasOwnProperty('toString')); // 输出 false,因为 'toString' 是从原型链继承的

两个方法除了调用方法不同外,还有以下区别:

  • hasOwnProperty 的参数必须是字符串。Object.hasOwn 的参数可以是字符串或符号。

  • hasOwnProperty 是 JavaScript 的老方法,兼容性非常好,几乎所有环境都支持。Object.hasOwn 是 ES2022 引入的,较新,可能在一些旧环境中不支持。

判断对象的原型(对象的类)

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

function Car(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);

console.log(auto instanceof Car);
// Expected output: true

console.log(auto instanceof Object);
// Expected output: true

有时会把对象的原型称为对象的类,例如 [] 是 Array 类的,实际上指的是 [] 的原型是 Array.prototype。(这里不是指 class 关键字创建的类)

参考

对象原型
https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes

继承与原型链
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#构造函数

instanceof
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/instanceof

本页目录