0

0

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

月夜之吻

月夜之吻

发布时间:2025-08-03 10:51:01

|

569人浏览过

|

来源于php中文网

原创

最准确的判断方式是在构造函数内部使用 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
属性也指向不同的原型对象。

Amazon Nova
Amazon Nova

亚马逊云科技(AWS)推出的一系列生成式AI基础模型

下载
<!-- 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()
和自定义标记则提供了更精细或更具控制力的替代方案。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
es6新特性
es6新特性

es6新特性有:1、块级作用域变量;2、箭头函数;3、模板字符串;4、解构赋值;5、默认参数;6、 扩展运算符;7、 类和继承;8、Promise。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

106

2023.07.17

es6新特性有哪些
es6新特性有哪些

es6的新特性有:1、块级作用域;2、箭头函数;3、解构赋值;4、默认参数;5、扩展运算符;6、模板字符串;7、类和模块;8、迭代器和生成器;9、Promise对象;10、模块化导入和导出等等。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

197

2023.08.04

JavaScript ES6新特性
JavaScript ES6新特性

ES6是JavaScript的根本性升级,引入let/const实现块级作用域、箭头函数解决this绑定问题、解构赋值与模板字符串简化数据处理、对象简写与模块化提升代码可读性与组织性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

233

2025.12.24

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1969

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

658

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2406

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

47

2026.01.19

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

77

2025.09.05

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

49

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
如何进行WebSocket调试
如何进行WebSocket调试

共1课时 | 0.1万人学习

TypeScript全面解读课程
TypeScript全面解读课程

共26课时 | 5.1万人学习

前端工程化(ES6模块化和webpack打包)
前端工程化(ES6模块化和webpack打包)

共24课时 | 5.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

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