JavaScript 如何判断 prototype 对象?

221 阅读6分钟

导读

在 JavaScript 中,prototype (原型)是一种用于对象继承的机制。每个函数都有一个 prototype 属性,这个属性是一个对象,用于构造函数创建的实例对象继承属性和方法。理解如何判断 prototype 对象以及一个对象是否是另一个对象的原型(即 prototype),是掌握 JavaScript 原型链和继承机制的关键。

什么是 prototype 对象?

在《理解 JavaScript 中的继承》一文中介原型和原型链的章节介绍过 prototype 对象。在 JavaScript 中,每个函数对象(function object)都有一个特殊的属性叫做 prototype。这个属性指向一个对象,该对象包含了由这个函数创建的实例对象所共享的属性和方法。举个例子:

function Person() {}

Person.prototype.greet = function() {
  console.log("Hello!");
};

let robert = new Person();
robert.greet();  // 输出: Hello!

在上面的代码中,Person.prototype 是一个对象,它有一个方法 greet,所有由 Person 构造函数创建的实例(如 robert)都可以访问这个方法。

prototype 对象的特性和用途

通过 prototype 对象的介绍,可以知道 prototype 对象是函数对象一个属性,更准确的说 prototype 对象是构造函数(Constructor Function)所特有的一个属性。

并且 prototype 对象的特点和用途主要包括以下几个方面:

共享属性和方法

prototype 对象上的属性和方法可以被构造函数实例化的所有对象实例共享。这意味着通过在 prototype 对象上定义方法和属性,可以节省内存,并确保所有实例对象访问的都是相同的方法或属性。

构造函数的原型

每个函数在创建时都会拥有一个 prototype 属性,这个属性指向一个对象,这个对象就是通过该构造函数创建的所有实例对象的原型。

原型链

JavaScript 中的对象通过 prototype 属性形成一个链条,称为原型链(Prototype Chain)。当访问对象的属性或方法时,JavaScript 引擎首先会在对象自身查找,如果没有找到,则会在对象的原型链上逐级查找,直到找到为止或者原型链的顶端(Object.prototype,其原型为 null)。

继承机制

prototype 是实现 JavaScript 继承机制的核心。通过修改构造函数的 prototype 对象,可以实现对象之间的属性和方法继承。例如,可以通过 Object.create() 方法创建一个新的对象,该对象的原型是指定的对象,从而实现继承。

动态性

在 JavaScript 中,prototype 对象是动态的,可以在运行时修改。这意味着可以在程序执行期间随时给 prototype 添加新的方法或属性,而这些修改会立即反映到所有依赖该原型的实例对象中。

Constructor 属性

每个 prototype 对象都有一个默认的 constructor 属性,该属性指向它的关联构造函数。通过这个属性,可以知道一个对象实例是由哪个构造函数创建的。

可扩展性

prototype 对象允许开发者在不直接修改构造函数代码的情况下,扩展其功能或添加新特性。通过直接操作 prototype 对象,可以在已有的类或构造函数上添加新的方法或属性。

判断一个对象是否是另一个对象的原型的方法

在了解 prototype 对象的特性和用途后,我再来了解一下在 JavaScript 中,判断一个对象是否是另一个对象的原型的方法。

使用 instanceof 运算符

instanceof 运算符用于测试一个对象是否是某个构造函数的实例,也可以间接用于判断一个对象是否存在于另一个对象的原型链上。

function Person() {}

let selina = new Person();

console.log(selina instanceof Person);  
// 输出: true
console.log(Person.prototype instanceof Object);  
// 输出: true

instanceof 检查 Person.prototype 是否在 selina 的原型链上。

使用 isPrototypeOf 方法

isPrototypeOf 方法可以直接检查一个对象是否在另一个对象的原型链上。它是 Object.prototype 上的方法。

function Person() {}

let bob = new Person();

console.log(Person.prototype.isPrototypeOf(bob));  
// 输出: true

在这里,Person.prototype.isPrototypeOf(bob) 返回 true,因为 bob 是通过 Person 构造函数创建的实例,并且 Person.prototype 存在于 bob 的原型链上。

使用 Object.getPrototypeOf 方法

Object.getPrototypeOf 方法返回指定对象的原型(即内部的 [[Prototype]] 属性)。

function Person() {}

let lily = new Person();

console.log(Object.getPrototypeOf(lily) === Person.prototype);  
// 输出: true

这种方法适用于直接比较对象的原型是否等于某个特定的原型对象。

如何判断对象是 prototype 对象?

目前我们了解了 prototype 对象的特性和一些检测对象的原型对象的方法,让我们来总提炼一下如何判断一个对象是 prototype 对象?

不难发现,判断对象是否为 prototype 对象的关键特性就是:prototype 对象的Constructor 属性。每个 prototype 对象都有一个默认的 constructor 属性,并且该属性指向它的关联构造函数

总结一下,关键点就是利用Constructor 属性Constructor 属性是构造函数,并且构造函数有 prototype 属性这 3 个主要特性。

isPrototype() 方法

import isConstructor from './isConstructor'

/**
 * 判断对象是否为 prototype 对象
 * ========================================================================
 * @method isPrototype
 * @param {Function|Object} obj - 要检测的数据
 * @returns {Boolean} 'obj' 是 prototype 对象,返回 true,否则返回 false
 */
const isPrototype = (obj) => {
  const OP = Object.prototype
  // prototype 对象有 constructor 属性
  const Ctor = obj ? obj.constructor : null
  // Ctor(constructor 属性)是构造函数
  // Ctor 构造函数又有 prototype 属性
  const proto = (isConstructor(Ctor) && Ctor.prototype) || OP

  return obj === proto
}

export default isPrototype

至于 isPrototype() 方法中使用的 isConstructor() 方法,请阅读《JavaScript 如何判断函数为构造函数?》一文了解详情。

总结

最后总结一下本文的内容要点:

判断一个对象是否为另一个对象的 prototype 可以使用 instanceof 运算符、isPrototypeOf 方法或 Object.getPrototypeOf 方法。每种方法都有其适用场景:

  • instanceof: 用于判断对象是否为某个构造函数的实例;
  • isPrototypeOf: 用于检查一个对象是否存在于另一个对象的原型链上;
  • Object.getPrototypeOf: 用于获取对象的直接原型,适合做精确的原型比较;

判断对象是否为 prototype 对象的关键特性:

  • prototype 对象有 constructor 属性;
  • constructor 属性是构造函数
  • 构造函数有 prototype 属性

了解这些方法有助于验证大家对于 JavaScript 的原型、原型链和基于原型链的继承机制理解和掌握程度。而通过理解 prototype 对象的这些特点,开发者可以更好地利用 JavaScript 的原型继承机制来构建高效、模块化的代码。