首页 > web前端 > js教程 > 正文

js如何判断两个对象原型相同

畫卷琴夢
发布: 2025-08-22 09:10:02
原创
663人浏览过

判断两个javascript对象是否拥有相同原型的最直接且推荐方式是使用 object.getprototypeof(obj1) === object.getprototypeof(obj2);2. 该方法通过获取对象的内部[[prototype]]引用并进行严格相等比较,确保结果准确可靠;3. object.getprototypeof() 是标准方法,语义明确且不受对象属性干扰,而 proto 因非标准、可被覆盖及性能问题不推荐在生产环境中使用;4. instanceof 不适合判断直接原型相同,因为它检查的是整个原型链中是否存在构造函数的 prototype,而非直接原型的同一性;5. 实际应用中,判断原型相同可用于精确类型检查、性能优化、序列化恢复对象行为以及元编程等场景,帮助开发者更精细地控制对象行为。

js如何判断两个对象原型相同

在JavaScript里,要判断两个对象是否拥有相同的原型,最直接且推荐的方式是使用

Object.getPrototypeOf()
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
方法,然后比较它们的结果是否严格相等。简单来说,就是
Object.getPrototypeOf(obj1) === Object.getPrototypeOf(obj2)
登录后复制
登录后复制
登录后复制

js如何判断两个对象原型相同

解决方案

判断两个JavaScript对象是否共享同一个原型,核心在于获取它们内部的

[[Prototype]]
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
引用,并进行比较。这个
[[Prototype]]
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
引用,就是我们常说的原型对象。

最标准、最推荐的方法是使用

Object.getPrototypeOf()
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
。这个方法会返回指定对象的原型(即其内部的
[[Prototype]]
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
属性的值)。因此,判断逻辑就变得非常清晰:

js如何判断两个对象原型相同
function haveSamePrototype(obj1, obj2) {
  // 确保传入的都是对象,或者至少不是原始值
  if (obj1 === null || typeof obj1 !== 'object' ||
      obj2 === null || typeof obj2 !== 'object') {
    // 原始值没有原型链的概念,或者说它们的“原型”是其包装对象的原型,但通常不这么比较
    // 这里的处理可以根据具体需求调整,比如抛出错误或返回false
    return false;
  }
  return Object.getPrototypeOf(obj1) === Object.getPrototypeOf(obj2);
}

// 示例:
let objA = {};
let objB = {};
console.log(haveSamePrototype(objA, objB)); // true (都继承自 Object.prototype)

function MyConstructor() {}
let instanceX = new MyConstructor();
let instanceY = new MyConstructor();
console.log(haveSamePrototype(instanceX, instanceY)); // true (都继承自 MyConstructor.prototype)

let arr1 = [];
let arr2 = [];
console.log(haveSamePrototype(arr1, arr2)); // true (都继承自 Array.prototype)

console.log(haveSamePrototype(objA, instanceX)); // false (一个继承自 Object.prototype,一个继承自 MyConstructor.prototype)
console.log(haveSamePrototype(objA, arr1)); // false (一个继承自 Object.prototype,一个继承自 Array.prototype)

// 特殊情况:null没有原型
console.log(Object.getPrototypeOf(Object.create(null))); // null
let nullProtoObj1 = Object.create(null);
let nullProtoObj2 = Object.create(null);
console.log(haveSamePrototype(nullProtoObj1, nullProtoObj2)); // true (都继承自 null,即没有原型)
登录后复制

这个方法的好处在于它直接、语义明确,并且是ECMAScript标准的一部分,因此在各种环境中都表现一致且可靠。它获取的是对象最直接的那个原型,也就是它从哪里继承属性和方法的。

Object.getPrototypeOf()
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
.__proto__
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
有什么区别

谈到获取对象原型,很多人可能会想到

.__proto__
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
这个属性。确实,在过去甚至现在,它被广泛用于访问对象的原型。然而,这两者之间存在显著的区别,理解这些差异对于编写健壮的JavaScript代码至关重要。

js如何判断两个对象原型相同

Object.getPrototypeOf()
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
是一个静态方法,属于
Object
登录后复制
登录后复制
登录后复制
登录后复制
构造函数。它的设计初衷就是为了提供一个标准、安全、统一的方式来获取任何给定对象的内部
[[Prototype]]
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
属性的值。它是一个函数调用,因此不会受到对象自身属性的影响,也不会在原型链上引发意外行为。你可以把它看作是官方推荐的“读”原型的方式。

.__proto__
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
则是一个访问器属性(accessor property),它通常存在于
Object.prototype
登录后复制
登录后复制
登录后复制
上。这意味着所有对象,如果它们的原型链最终能追溯到
Object.prototype
登录后复制
登录后复制
登录后复制
,那么它们理论上都可以通过
.__proto__
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
来访问自己的原型。然而,它的问题在于:

  1. 非标准性(历史遗留):虽然现代浏览器和Node.js都广泛支持
    .__proto__
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    ,但它最初并非ECMAScript标准的一部分,而是Mozilla引入的一个特性,后来才被附录B纳入标准,作为遗留特性。这意味着在某些严格的ES环境中,它可能不被支持或行为不一致。
  2. 性能问题:直接操作
    .__proto__
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    来修改对象的原型,通常会带来显著的性能开销,因为它可能导致V8等JavaScript引擎对对象进行“去优化”(de-optimization)。修改一个对象的
    [[Prototype]]
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    是一个非常昂贵的操作,因为它会改变对象的内部结构。
  3. 可被覆盖:由于
    .__proto__
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    是一个属性,它可以在对象自身或其原型链的任何位置被覆盖(shadowed)。这意味着你可能以为你在访问原型,但实际上却在访问一个普通的属性。例如:
    let myObj = {
      __proto__: '我是一个普通属性,不是原型!'
    };
    console.log(myObj.__proto__); // '我是一个普通属性,不是原型!'
    console.log(Object.getPrototypeOf(myObj)); // [Object: null prototype] {} (这是myObj真正的原型,一个空对象字面量的原型)
    登录后复制

    这种情况下,

    .__proto__
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    的值就不是真正的原型了。
    Object.getPrototypeOf()
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    则总是能获取到正确的原型。

所以,我的建议是:如果你只是想安全地读取一个对象的原型,始终使用

Object.getPrototypeOf()
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
。如果你需要修改一个对象的原型,应该使用
Object.setPrototypeOf()
登录后复制
(同样有性能考量,非必要不推荐)。而
.__proto__
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
呢,可以作为一种快速的、非生产环境下的调试工具,或者在了解其局限性后,偶尔用于一些旧代码的兼容性处理。但在新代码中,请尽量避免直接使用它。

为什么
instanceof
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
不适合直接判断两个对象原型是否相同?

instanceof
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
操作符在JavaScript中扮演着一个非常重要的角色,它用于检查一个对象是否是某个构造函数的实例。但它与判断两个对象是否拥有“相同原型”是两个完全不同的概念,尽管它们都与原型链有关。

instanceof
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
的工作原理是这样的:它会检查
Object
登录后复制
登录后复制
登录后复制
登录后复制
的整个原型链,看
constructor.prototype
登录后复制
是否出现在
Object
登录后复制
登录后复制
登录后复制
登录后复制
的原型链中的任何位置。换句话说,它关注的是“
Object
登录后复制
登录后复制
登录后复制
登录后复制
是不是
constructor
登录后复制
或其父类的实例?”。它是一个关于继承关系的检查,而非直接的原型身份(identity)检查。

举个例子,你可能会觉得:

function Foo() {}
let a = new Foo();
let b = new Foo();
console.log(a instanceof Foo && b instanceof Foo); // true
console.log(Object.getPrototypeOf(a) === Object.getPrototypeOf(b)); // true
登录后复制

看起来好像都能判断。但考虑以下情况:

function Parent() {}
function Child() {}
Child.prototype = Object.create(Parent.prototype); // Child继承Parent

let obj1 = new Child();
let obj2 = new Parent();

console.log(Object.getPrototypeOf(obj1) === Object.getPrototypeOf(obj2)); // false
// obj1 的直接原型是 Child.prototype
// obj2 的直接原型是 Parent.prototype
// 它们当然不相同

console.log(obj1 instanceof Parent); // true (因为 Parent.prototype 在 obj1 的原型链上)
console.log(obj2 instanceof Parent); // true
登录后复制

在这个例子中,

obj1
登录后复制
登录后复制
登录后复制
登录后复制
Child
登录后复制
的实例,
obj2
登录后复制
Parent
登录后复制
登录后复制
的实例。它们各自的直接原型是
Child.prototype
登录后复制
登录后复制
Parent.prototype
登录后复制
登录后复制
登录后复制
,这两个原型对象显然是不同的。所以
Object.getPrototypeOf(obj1) === Object.getPrototypeOf(obj2)
登录后复制
登录后复制
登录后复制
返回
false
登录后复制
,这符合我们的预期。

然而,

obj1 instanceof Parent
登录后复制
却返回
true
登录后复制
。这是因为
instanceof
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
会沿着
obj1
登录后复制
登录后复制
登录后复制
登录后复制
的原型链向上查找,发现
Parent.prototype
登录后复制
登录后复制
登录后复制
确实存在于其原型链上(
obj1
登录后复制
登录后复制
登录后复制
登录后复制
->
Child.prototype
登录后复制
登录后复制
->
Parent.prototype
登录后复制
登录后复制
登录后复制
->
Object.prototype
登录后复制
登录后复制
登录后复制
)。这说明
obj1
登录后复制
登录后复制
登录后复制
登录后复制
Parent
登录后复制
登录后复制
的一个子类实例。

所以,

instanceof
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
回答的是“这个对象是不是由这个构造函数(或其子类)创建的?”而
Object.getPrototypeOf(obj1) === Object.getPrototypeOf(obj2)
登录后复制
登录后复制
登录后复制
回答的是“这两个对象是不是直接从同一个原型对象继承而来的?”。它们的侧重点和应用场景完全不同。如果你想知道两个对象是否拥有完全相同的直接“基因”,那么
Object.getPrototypeOf()
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
的比较才是正确的选择。

实际应用场景中,判断原型相同有哪些用途?

虽然听起来有点抽象,但在实际的JavaScript开发中,判断两个对象是否拥有相同的原型,其实有不少实用场景,尤其是在你需要对对象进行更细粒度的类型检查、优化或者内部机制设计时。

  1. 精确的类型检查与行为预判:

    typeof
    登录后复制
    登录后复制
    instanceof
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    登录后复制
    不够用时,判断原型相同可以提供更精确的“类型”识别。例如,你可能有一个内部的“工厂函数”或“私有构造器”,它创建的对象都应该共享同一个原型。如果你需要确保某个对象确实是由这个特定工厂函数产生的,而不是通过其他方式(比如
    Object.create()
    登录后复制
    或一个外部的、行为类似的构造器),那么比较它们的直接原型就非常有效。

    function createMyInternalObject() {
      // 内部逻辑...
      return { /* ... */ }; // 假设这里返回的对象都隐式地拥有某个特定原型
    }
    // 假设内部实现会确保返回的对象共享一个特定原型,例如通过 Object.setPrototypeOf 或直接返回一个特定构造器的实例
    // 简化:
    const MyPrivateProto = {
        _tag: 'MyInternalType',
        doSomething() { console.log('Doing internal stuff.'); }
    };
    function createMyInternalObjectV2() {
        return Object.create(MyPrivateProto);
    }
    
    let objA = createMyInternalObjectV2();
    let objB = createMyInternalObjectV2();
    let objC = {}; // 普通对象
    
    if (Object.getPrototypeOf(objA) === MyPrivateProto) {
        console.log("objA 是我们内部创建的对象,可以调用其内部方法。");
        objA.doSomething();
    }
    console.log(Object.getPrototypeOf(objA) === Object.getPrototypeOf(objB)); // true
    console.log(Object.getPrototypeOf(objA) === Object.getPrototypeOf(objC)); // false
    登录后复制

    这在处理内部API、框架组件或特定数据结构时特别有用,它确保了对象不仅“看起来”像某种类型,而且确实是该类型家族的成员。

  2. 优化与性能考量: 在某些高性能要求的场景下,如果确定一组对象共享同一个原型,你可能会进行一些优化。例如,如果一个函数接收多个参数,并且你知道这些参数都是由同一个构造函数创建的,那么它们共享相同的方法集。这样,你可以避免不必要的属性查找或类型检查,直接调用原型上的方法,从而可能带来微小的性能提升。这在游戏开发、图形渲染或大数据处理等领域,对大量相似对象进行迭代操作时可能会被考虑。

  3. 序列化与反序列化: 当你需要将JavaScript对象序列化为JSON字符串,然后再反序列化回来时,默认情况下,方法和原型链上的属性是不会被序列化的。如果你的对象依赖于其原型上的方法,那么简单地

    JSON.parse()
    登录后复制
    可能会导致反序列化后的对象失去其行为。 通过在反序列化过程中判断(或强制设置)对象的原型,可以确保恢复的对象具有正确的行为。

    function MyClass() { this.value = 10; }
    MyClass.prototype.getValue = function() { return this.value; };
    
    let instance = new MyClass();
    let jsonString = JSON.stringify(instance); // {"value":10}
    let parsedObject = JSON.parse(jsonString);
    
    console.log(parsedObject.getValue); // undefined
    
    // 假设我们知道这是MyClass的实例
    if (Object.getPrototypeOf(parsedObject) !== MyClass.prototype) {
        Object.setPrototypeOf(parsedObject, MyClass.prototype); // 重新设置原型
    }
    console.log(parsedObject.getValue()); // 10
    登录后复制

    当然,这只是一个简化示例,实际中更复杂的反序列化可能需要更精妙的策略,但原型比较是其中一个环节。

  4. 元编程与反射: 在一些高级的JavaScript应用,比如构建ORM(对象关系映射)、依赖注入框架或者自定义的类型系统时,你可能需要检查或操作对象的原型链。判断原型是否相同,是进行这些“元操作”的基础。它允许你动态地分析对象的结构和行为,并根据需要进行调整。

总的来说,判断原型相同,是深入理解和控制JavaScript对象行为的一个关键工具。它提供了比

typeof
登录后复制
登录后复制
instanceof
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
登录后复制
更细粒度的控制,让你能够更精确地处理对象的“血统”问题。

以上就是js如何判断两个对象原型相同的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号