对于初学者来说,“对象属性拷贝”这个概念一开始可能会有点抽象,特别是和 DTO (Data Transfer Object) 一起出现时。
别担心,这个概念本身并不复杂。它其实就是解决一个很常见的编程问题:如何方便、安全地把数据从一个“篮子”(对象A)装到另一个“篮子”(对象B)里。
📝 关于「对象属性拷贝」的学习笔记
你好!很高兴你开始学习 DTO 相关的知识。在深入 DTO 之前,我们必须先掌握一个基础且非常实用的技能:对象属性拷贝 (Object Property Copying)。
📌 1. 为什么需要这东西?从一个故事说起
想象一下,你正在开发一个网站。后端程序从数据库里查询出了一个用户(User)的完整信息。这个 User
对象可能长这样:
JavaScript
|
|
现在,前端页面需要显示用户的基本信息(ID、用户名、邮箱)。我们是不是应该直接把 userFromDB
这个对象整个发给前端呢?
绝对不行! 🙅♂️
如果我们直接发送,就会把 password
(密码)和 role
(角色)这种敏感信息也泄露给前端了。这是非常危险的。
所以,我们需要一个“中间人”,一个专门用来在网络上传输数据的“小包裹”。这个小包裹就是 DTO (Data Transfer Object)。它只包含前端需要的数据:
JavaScript
|
|
那么问题来了:如何把 userFromDB
里的 id
, username
, email
的值,填到 userForFrontEnd
这个新对象里呢?
这就是“对象属性拷贝”大显身手的时刻!
核心思想:对象属性拷贝,就是将一个源对象 (Source Object) 的属性值,复制到另一个目标对象 (Target Object) 的同名属性中。
💡 2. 怎么拷贝?从手动到自动
2.1 最笨拙的方法:手动拷贝
最直观的方法,就是一个一个地赋值。
JavaScript
|
|
优点:
-
非常清晰,一目了然。
-
可以精确控制拷贝哪些属性,忽略不需要的属性(比如
password
)。
缺点:
- 如果对象有50个属性,手动写50行代码会非常繁琐且容易出错。
2.2 聪明一点的方法:使用工具库
几乎所有的编程语言都提供了简化这个过程的工具。在 Java 中有 BeanUtils.copyProperties()
,在 JavaScript 中有 Object.assign()
或扩展运算符 ...
。它们可以自动拷贝所有同名的属性。
以 JavaScript 的 Object.assign()
为例:
JavaScript
|
|
用这个方法来解决我们最初的问题:
这种工具非常适合将一个简单的字典(比如前端发来的请求体)的属性拷贝到一个对象上。但是,从 Entity
拷贝到 DTO
时,由于我们想忽略某些属性,直接使用 Object.assign
仍然会把 password
等敏感属性拷贝过去。
因此,更专业的做法是使用专门的 Mapper (映射器) 库,例如 Java 的 MapStruct、ModelMapper,或者 .NET 的 AutoMapper。这些库可以让你配置映射规则,比如:“把 User
拷贝到 UserDTO
时,请忽略 password
字段”。
⚠️ 3. 重要的区别:浅拷贝 vs. 深拷贝
这是学习属性拷贝时必须理解的一个关键点,也是面试中经常被问到的问题。
3.1 浅拷贝 (Shallow Copy)
想象一下,你的对象里有一个属性,它的值本身也是一个对象。
JavaScript
|
|
浅拷贝的特点:
-
它只拷贝对象的第一层属性。
-
如果某个属性的值是一个基础类型(如数字、字符串),它会拷贝这个值。
-
如果某个属性的值是一个引用类型(如另一个对象、数组),它只会拷贝这个引用的“地址”,而不是创建一个新的对象。
后果是什么?
user
和 copiedUser
的 address
属性指向的是同一个内存地址里的同一个 { city: "Beijing", ... }
对象。
如果你修改 copiedUser
的地址,user
的地址也会跟着变!
JavaScript
|
|
适用场景:当你的对象结构很简单,没有嵌套的对象或数组时,浅拷贝速度快,效率高,完全够用。我们之前
User
到UserDTO
的转换,就属于这种情况。
3.2 深拷贝 (Deep Copy)
深拷贝会彻底地、递归地复制一个对象的所有层级。
JavaScript
|
|
深拷贝的特点:
-
它会创建一个全新的、一模一样但完全独立的对象。
-
如果遇到嵌套的对象,它会为这个嵌套对象也创建一个全新的副本。
后果是什么?
user
和 deepCopiedUser
内部的所有部分都是独立的,互不影响。
如果你再次修改地址:
JavaScript
|
|
适用场景:当你需要一个对象的完整、独立的副本,并且不希望任何对副本的修改影响到原始对象时,就必须使用深拷贝。这在复杂的状态管理(如前端框架 Vuex/Redux)或需要历史记录功能时非常重要。
✅ 总结与回顾
-
是什么:对象属性拷贝就是把一个对象(源)的属性值,复制到另一个对象(目标)上。
-
为什么用:最常见的场景是为了构建 DTO。从包含敏感信息的完整对象(Entity)中,只挑选出安全、必需的属性,拷贝到一个新的 DTO 对象中,用于网络传输或视图展示。
-
怎么做:
-
手动拷贝:简单、可控,但繁琐。
-
工具/库:如
Object.assign
,可以自动拷贝同名属性。更专业的有 Mapper 库,可以配置复杂的映射规则。
-
-
关键区别:
-
浅拷贝 (Shallow Copy):只拷贝第一层,速度快。嵌套的对象共享同一个引用,修改一个会影响另一个。
-
深拷贝 (Deep Copy):递归拷贝所有层,生成完全独立的新对象,互不干扰,但性能开销更大。
-
记住,它不是一个孤立的技术,而是编程中为了实现数据隔离、安全和封装而广泛使用的一种基本模式。