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

js怎么判断对象是否通过new创建

月夜之吻
发布: 2025-08-03 10:51:01
原创
559人浏览过

最准确的判断方式是在构造函数内部使用 new.target,若函数通过 new 调用则 new.target 指向构造函数,否则为 undefined;2. 对于已创建的对象,可使用 instanceof 判断其原型链是否包含构造函数的 prototype,但该方法在跨 realm 或原型链被修改时会失效;3. 可通过 object.getprototypeof() 或 isprototypeof() 更精确地检查原型链关系;4. constructor 属性不可靠,因其可被修改;5. 可在构造函数中添加自定义标记(如 this._isnewinstance = true)来实现可靠的“事后”判断,前提是能控制构造逻辑。最终,最推荐的方案是在构造时用 new.target 控制行为,外部判断则结合 instanceof 与原型检查并注意其局限性。

js怎么判断对象是否通过new创建

在JavaScript里,要百分之百地“事后”判断一个对象是不是通过

new
登录后复制
关键字创建的,这事儿有点微妙,甚至可以说,没有一个完美的银弹。最直接、最准确的方式,其实是在构造函数内部利用
new.target
登录后复制
这个元属性来判断。如果对象已经创建出来了,我们通常会借助
instanceof
登录后复制
操作符,或者通过检查其原型链来推断,但这两种方法都有其局限性。

js怎么判断对象是否通过new创建

解决方案

判断一个对象是否通过

new
登录后复制
创建,最直接且推荐的做法是在其构造函数内部使用
new.target
登录后复制
。这个ES6引入的特性,会在函数被
new
登录后复制
调用时指向被
new
登录后复制
调用的构造函数,如果函数是普通调用,则
new.target
登录后复制
undefined
登录后复制
。这让我们可以精确地控制构造函数的行为,甚至强制它必须通过
new
登录后复制
来调用。

举个例子,假设我们有一个

Person
登录后复制
构造函数:

js怎么判断对象是否通过new创建
function Person(name) {
  // 在这里判断 new.target
  if (!new.target) {
    // 如果没有使用 new 调用,则返回一个通过 new 创建的实例
    console.warn("建议使用 new 关键字创建 Person 实例。");
    return new Person(name);
  }
  this.name = name;
  console.log(`Person 实例 ${this.name} 已创建。`);
}

const person1 = new Person('Alice'); // 正常创建
const person2 = Person('Bob');       // 尝试不使用 new 创建,会被内部修正
console.log(person1 instanceof Person); // true
console.log(person2 instanceof Person); // true
登录后复制

这种模式,在我日常写一些库或者框架的时候,用得特别多,它能有效避免用户误操作导致的问题。

如果对象已经创建,且我们无法修改其构造函数,那么

instanceof
登录后复制
是最常用的方法。它会检查对象的原型链上是否存在指定构造函数的
prototype
登录后复制
属性。

js怎么判断对象是否通过new创建
function Animal(type) {
  this.type = type;
}

const dog = new Animal('Dog');
const cat = new Animal('Cat');

console.log(dog instanceof Animal); // true
console.log(cat instanceof Animal); // true

const obj = {};
console.log(obj instanceof Animal); // false
登录后复制

instanceof
登录后复制
并非没有缺陷,它依赖于原型链的完整性,如果原型链被修改或者对象来自不同的 JavaScript Realm (比如 iframe),它可能就不那么可靠了。

深入理解
new.target
登录后复制
:如何精准控制构造行为?

new.target
登录后复制
是一个非常精妙的语言特性,它不仅仅是用来判断“是否通过
new
登录后复制
创建”,更重要的是,它赋予了构造函数内部一个强大的自我感知能力。当一个函数被
new
登录后复制
操作符调用时,
new.target
登录后复制
会指向这个
new
登录后复制
表达式中直接被调用的构造函数。如果函数是作为普通函数被调用(没有
new
登录后复制
),那么
new.target
登录后复制
的值就是
undefined
登录后复制

这有什么用呢?在我看来,它最大的价值在于“防御性编程”和“弹性构造”。

防御性编程:强制使用

new
登录后复制

就像前面

Person
登录后复制
例子里展示的,我们可以利用
!new.target
登录后复制
来强制用户必须使用
new
登录后复制
来实例化对象。这避免了因为忘记
new
登录后复制
导致
this
登录后复制
指向全局对象(在非严格模式下)或者报错(在严格模式下),从而引发难以追踪的bug。

function Product(name, price) {
  if (!new.target) {
    // 没用 new?那我帮你 new 一个
    console.warn("Product 构造函数必须通过 new 关键字调用!");
    return new Product(name, price);
  }
  this.name = name;
  this.price = price;
}

const p1 = new Product('Laptop', 1200); // 正常
const p2 = Product('Mouse', 25);       // 会被自动修正并警告
console.log(p1.name, p2.name);
登录后复制

这种做法,让你的API接口变得更加健壮,减少了用户的犯错空间。

弹性构造:根据调用方式调整行为

除了强制,

new.target
登录后复制
还能实现更灵活的构造逻辑。例如,你可以让同一个函数在
new
登录后复制
调用时作为一个构造函数,而在普通调用时作为一个工厂函数,返回一个预设的实例或者其他什么东西。

function Greeter(greeting) {
  if (new.target) {
    // 如果是 new 调用,初始化实例
    this.greeting = greeting;
    console.log("Greeter 实例被构造");
  } else {
    // 如果是普通调用,返回一个默认的 Greeter 实例
    console.log("返回一个默认 Greeter 实例");
    return new Greeter("Hello, default!");
  }
}

const myGreeter = new Greeter("Hi there!");
console.log(myGreeter.greeting); // "Hi there!"

const defaultGreeter = Greeter();
console.log(defaultGreeter.greeting); // "Hello, default!"
登录后复制

这让你的函数能够适应不同的使用场景,提供更友好的API。当然,这种灵活性也可能增加一点点理解成本,所以在使用时需要权衡。

instanceof
登录后复制
并非万能:何时会失灵?

instanceof
登录后复制
操作符在日常开发中非常常用,它通过检查一个对象的原型链(
[[Prototype]]
登录后复制
内部属性)是否包含某个构造函数的
prototype
登录后复制
属性来判断实例关系。听起来很直观,但它确实有自己的局限性,尤其是在一些复杂或跨环境的场景下。

一个很常见的场景就是跨 Realm (跨域/iframe) 对象。每个 JavaScript Realm(比如一个浏览器窗口、一个 iframe 或者 Node.js 的 vm 模块创建的上下文)都有自己独立的全局对象和内置构造函数。这意味着,一个在 iframe A 中创建的对象,即使它是

Array
登录后复制
类型,在 iframe B 中使用
instanceof Array
登录后复制
来判断,结果也可能是
false
登录后复制
。因为这两个
Array
登录后复制
构造函数是不同的对象,它们的
prototype
登录后复制
属性也指向不同的原型对象。

通义万相
通义万相

通义万相,一个不断进化的AI艺术创作大模型

通义万相596
查看详情 通义万相
<!-- index.html -->
<iframe id="myFrame" src="about:blank"></iframe>
<script>
  const iframe = document.getElementById('myFrame');
  const iframeDoc = iframe.contentWindow.document;
  iframeDoc.write('<script>parent.myArrayInIframe = [];</script>');
  iframeDoc.close();

  setTimeout(() => {
    const arrInParent = [];
    const arrInIframe = window.myArrayInIframe;

    console.log(arrInParent instanceof Array); // true (在当前 Realm)
    console.log(arrInIframe instanceof Array); // false (来自不同 Realm 的 Array 构造函数)
  }, 100);
</script>
登录后复制

你看,即使它们看起来都是数组,

instanceof
登录后复制
却给出了不同的结果。这在前端开发中,尤其是在涉及微前端或者嵌入第三方内容的场景下,是个需要特别注意的“坑”。

另一个是原型链被修改的情况。虽然不常见,但如果有人手动修改了对象的原型链,

instanceof
登录后复制
的结果也会变得不可靠。

function CustomObject() {}
const obj = new CustomObject();

// 正常情况
console.log(obj instanceof CustomObject); // true

// 修改原型链
Object.setPrototypeOf(obj, Object.prototype);
console.log(obj instanceof CustomObject); // false (原型链被截断,CustomObject.prototype 不再在其中)
登录后复制

最后,如果你的目标是判断一个对象是不是“某个类型”的实例,而不是特指“通过

new
登录后复制
某个构造函数创建”,那么
instanceof
登录后复制
也可能不是最佳选择。比如,你可能想检查一个对象是否“可迭代”,这时候会更倾向于检查它是否有
Symbol.iterator
登录后复制
方法,而不是它是不是
Array
登录后复制
Map
登录后复制
的实例。

除了
new.target
登录后复制
instanceof
登录后复制
,还有哪些辅助方法?

当我们无法使用

new.target
登录后复制
(因为对象已经创建且我们无法修改构造函数),并且
instanceof
登录后复制
又不够可靠时,还有一些辅助方法可以帮助我们进行更深层次的判断,或者说,从不同的角度来理解一个对象的“来源”或“类型”。

1. 使用

Object.getPrototypeOf()
登录后复制
检查原型链

Object.getPrototypeOf()
登录后复制
是一个非常实用的方法,它直接返回指定对象的原型(即
[[Prototype]]
登录后复制
内部属性的值)。结合
Object.prototype.isPrototypeOf()
登录后复制
,我们可以更灵活地检查一个对象是否在另一个对象的原型链上。

function MyConstructor() {}
const myInstance = new MyConstructor();

// 检查 myInstance 的原型链上是否存在 MyConstructor.prototype
console.log(MyConstructor.prototype.isPrototypeOf(myInstance)); // true

// 也可以这样组合判断,虽然和 instanceof 效果类似,但理解起来更直接
console.log(Object.getPrototypeOf(myInstance) === MyConstructor.prototype); // true
// 注意:这只检查直接原型,如果中间有继承,则不为 true
// 而 isPrototypeOf 会检查整个原型链
登录后复制

isPrototypeOf
登录后复制
instanceof
登录后复制
更底层,因为它不涉及
constructor
登录后复制
属性或
Symbol.hasInstance
登录后复制
,只是纯粹地检查原型链关系。这在某些需要精确控制原型检查的场景下很有用。

2. 检查

constructor
登录后复制
属性(谨慎使用!)

每个对象通常都有一个

constructor
登录后复制
属性,指向创建该实例的构造函数。

function AnotherConstructor() {}
const anotherInstance = new AnotherConstructor();

console.log(anotherInstance.constructor === AnotherConstructor); // true
console.log(anotherInstance.constructor.name); // "AnotherConstructor"
登录后复制

然而,这个方法非常不推荐作为判断对象是否通过

new
登录后复制
创建的可靠依据。为什么呢?因为
constructor
登录后复制
属性是可写的,它很容易被修改、覆盖,或者在继承链中指向非预期的构造函数。

function Parent() {}
function Child() {}
Child.prototype = new Parent(); // 继承 Parent 的原型,但 constructor 没改
Child.prototype.constructor = Child; // 修正 constructor

const childInstance = new Child();
console.log(childInstance.constructor === Child);   // true
console.log(childInstance.constructor === Parent);  // false
console.log(childInstance instanceof Child);        // true
console.log(childInstance instanceof Parent);       // true

// 如果 constructor 没修正,或者被恶意修改
function BadConstructor() {}
const badInstance = new BadConstructor();
badInstance.constructor = String; // 随便改
console.log(badInstance.constructor === BadConstructor); // false
console.log(badInstance instanceof BadConstructor);      // true
登录后复制

所以,

constructor
登录后复制
属性更多是提供一个“线索”,而不是一个可靠的“证据”。

3. 添加自定义标记或属性

在某些特定场景下,如果你的设计允许,你可以在构造函数内部给实例添加一个私有(或伪私有)的标记,来明确它是否是通过

new
登录后复制
创建的。

function TaggedObject() {
  if (!new.target) {
    return new TaggedObject();
  }
  this._isNewInstance = true; // 添加一个标记
  // ... 其他初始化
}

const tagInstance = new TaggedObject();
const nonTagInstance = TaggedObject(); // 也会被 new

console.log(tagInstance._isNewInstance);    // true
console.log(nonTagInstance._isNewInstance); // true
登录后复制

这种方法完全依赖于你自己的约定和实现,但它在你知道所有可能的创建路径时,可以提供最直接、最可靠的“事后”判断依据。这就像在产品出厂时贴一个“合格证”,简单直接。

总的来说,判断一个对象是否通过

new
登录后复制
创建,最佳时机是在构造函数内部使用
new.target
登录后复制
。如果是在外部判断,
instanceof
登录后复制
是最常用的,但要警惕其局限性。而
Object.getPrototypeOf()
登录后复制
和自定义标记则提供了更精细或更具控制力的替代方案。

以上就是js怎么判断对象是否通过new创建的详细内容,更多请关注php中文网其它相关文章!

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

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

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

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