黑洞来咯
89.71MB · 2026-02-15
让我们先看两段代码。
Java 代码:
CountPointsTransactDto record = new CountPointsTransactDto();
record.amount = 100;
TypeScript 代码:
const record = new CountPointsTransactDto();
record.amount = 100;
乍一看,这简直就是双胞胎。语法结构、关键字、甚至赋值方式都如出一辙。很多从 Java 转过来的后端同学看到这里会松一口气:“切,TS 不就是带类型的 JS 嘛,跟写 Java 没区别。”
大错特错。
虽然它们长得像,但在计算机内存的微观世界里,这两行代码触发的逻辑完全属于两个不同的宇宙。
new:严格的蓝图 (Blueprint)在 Java 中,类(Class)是一张不可修改的工程蓝图。
当你执行 new 时,JVM 会做以下事情:
.class 文件,解析字段和方法。结论:Java 对象出生那一刻,它的结构就定死了。你不可能在运行时突然给它加一个 nickname 属性。如果你敢这么做,编译器会直接报错,IDE 会标红。
new:可塑的黏土 (Clay)在 TypeScript(最终运行的是 JavaScript)中,类只是一个函数,对象只是一个哈希表(Key-Value Map) 。
当你执行 new 时,JS 引擎做了这 4 件事:
{}。__proto__ 指针指向类的 prototype(为了能用类的方法)。this.amount = 0)。结论:JS 对象本质上是一团可以随意揉捏的黏土。
虽然 TypeScript 的编译器(tsc)会像 Java 一样检查你的拼写,但在运行时,你完全可以给这个对象追加任何属性(record.whatever = 123),JS 引擎绝不会拦你。
Java 能不能直接赋值,取决于访问修饰符。
amount 是 public,可以。private 的,必须通过 setAmount() 方法来访问。if (amount < 0) throw error),保证数据安全。TypeScript 默认所有属性都是 public。
在 TS/JS 生态中,直接操作属性(record.amount = 100)是标准做法。
getAmount() / setAmount()。private 关键字,但那只是编译时的约束,编译成 JS 后,私有属性依然可以被访问(虽然不推荐)。这是最颠覆 Java 开发者认知的一点。
Java 只认名字(身份证)。
哪怕两个类长得一模一样,名字不一样,就是不兼容。
class A { int x; }
class B { int x; }
A obj = new B(); // 报错!B 不是 A。
TypeScript 只认长相(鸭子测试)。
只要你长得像(属性列表匹配),你就是它。
class A { x: number; }
class B { x: number; }
const obj: A = new B(); // 通过!因为 B 也有 x,结构满足 A 的要求。
这也是为什么在 TS 里,你经常看到有人偷懒,不用 new,而是直接写个字面量对象:
// TS 允许这样(只要属性对得上)
const record: CountPointsTransactDto = { amount: 100 };
new?既然 { amount: 100 } 就能冒充 DTO,为什么我们在 NestJS 中还是推荐写:
const record = new CountPointsTransactDto();
原因有三:
初始值 (Default Values) :
类里定义了 status = 'PENDING',new 出来的对象自动就有。字面量 {} 必须手动写一遍。
方法 (Methods) :
只有 new 出来的实例才挂载了原型链,才能调用 DTO 里定义的 isValid() 或 calculateTax() 方法。
元数据 (Metadata & Decorators) :
这是最重要的。NestJS 大量使用装饰器(如 @IsString(), @Expose())。
纯 JSON 对象是不带这些装饰器信息的。只有通过 class-transformer 的 plainToInstance 或者直接 new 出来的对象,验证管道(ValidationPipe)才能正常工作。
| 特性 | Java (new) | TypeScript / JS (new) |
|---|---|---|
| 本质 | 按照蓝图开辟固定内存块 | 创建空哈希表,链接原型链 |
| 结构灵活性 | 不可变 (编译后固定) | 高度可变 (运行时可增删属性) |
| 属性赋值 | 依赖 public/private,常用 Setter | 默认 public,常用直接赋值 |
| 类型兼容 | 看名字 (必须是同一个类或子类) | 看结构 (属性匹配即可) |
| 使用建议 | 必须用 new | 推荐用 new (为了默认值和装饰器) |
一句话总结:
不要被 TypeScript 的语法糖欺骗了。它虽然穿上了 Java 的西装(Class, new, private),但它的灵魂依然是那个自由、灵活、基于原型的 JavaScript。理解了这一点,你写出的 TS 代码才会有真正的“TS 味”。
同一个 new,不同的世界:Java 与 TypeScript 对象创建机制的降维打击