导读
在 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 的原型继承机制来构建高效、模块化的代码。