作者简介: 前端领域优质创作者
资源导航: 传送门=>
个人主页: 江城开朗的豌豆
个人专栏:《VUE》 《JavaScript》
个人网站: 江城开朗的豌豆
个人邮箱: [email protected]
个人微信: y_t_t_t_
座 右 铭: 生活就像心电图,一帆风顺就证明你挂了。
QQ群: 906392632 (前端技术交流群)
欢迎来到我的个人主页!这里是我分享技术心得和生活感悟的地方。希望你能在这里找到有价值的内容,也欢迎随时联系我交流讨论!
目录
基本概念:赋值 vs 拷贝
赋值操作
浅拷贝的实现方式
1. 展开运算符(...)
2. Object.assign()
3. 数组的浅拷贝方法
深拷贝的实现方式
1. JSON.parse(JSON.stringify())
2. 使用第三方库
3. 手动实现深拷贝
实际应用场景
何时使用浅拷贝?
何时使用深拷贝?
常见问题与解决方案
1. 循环引用问题
2. 特殊类型的处理
3. 性能优化
框架中的拷贝实践
React中的状态更新
Vue中的响应式数据
总结
在JavaScript开发中,数据复制是日常工作中的常见操作。本文将深入探讨JavaScript中的深浅拷贝机制,帮助开发者避免常见的陷阱。
let user = {name: 'John', skills: ['JavaScript', 'React']};
let colleague = user;
colleague.name = 'Mike';
console.log(user.name); // 输出: 'Mike' - 原对象被修改了
赋值操作只是创建了一个新的引用,指向同一个内存地址,修改任一变量都会影响另一个。
浅拷贝只复制对象的第一层属性,对于嵌套对象则复制引用。
let user = {name: 'John', skills: ['JavaScript', 'React']};
let userCopy = {...user};
userCopy.name = 'Alice';
console.log(user.name); // 输出: 'John' - 第一层属性不受影响
userCopy.skills.push('Vue');
console.log(user.skills); // 输出: ['JavaScript', 'React', 'Vue'] - 嵌套对象被修改
let user = {name: 'John', skills: ['JavaScript', 'React']};
let userClone = Object.assign({}, user);
let skills = ['JavaScript', 'React'];
let skillsCopy = skills.slice();
// 或
let skillsCopy2 = [...skills];
// 或
let skillsCopy3 = Array.from(skills);
深拷贝会递归复制对象的所有层级,创建一个完全独立的副本。
let user = {
name: 'John',
skills: ['JavaScript', 'React'],
info: {age: 25, level: 'Senior'}
};
let userDeepCopy = JSON.parse(JSON.stringify(user));
userDeepCopy.skills.push('Vue');
console.log(user.skills); // 输出: ['JavaScript', 'React'] - 原对象不受影响
局限性:
不能处理函数、Symbol、undefined等特殊类型
会丢失对象的原型链
不能处理循环引用
Lodash的_.cloneDeep()
是更可靠的深拷贝方案:
import _ from 'lodash';
let user = {
name: 'John',
skills: ['JavaScript', 'React'],
sayHi: function() { console.log('Hi!'); }
};
let userDeepCopy = _.cloneDeep(user);
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (hash.has(obj)) return hash.get(obj);
let clone = Array.isArray(obj) ? [] : {};
hash.set(obj, clone);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key], hash);
}
}
return clone;
}
let user = {name: 'John', skills: ['JavaScript', 'React']};
let userClone = deepClone(user);
对象结构简单,没有嵌套属性
需要共享部分数据引用以节省内存
性能敏感场景(深拷贝开销较大)
// 表单初始值场景
const initialForm = {username: '', remember: false};
let formData = {...initialForm}; // 浅拷贝足够
复杂嵌套对象需要完全独立副本
状态管理(如Redux reducer中)
需要修改副本而不影响原对象
// Redux reducer示例
function reducer(state = initialState, action) {
switch(action.type) {
case 'UPDATE_SKILLS':
return {
...state,
user: {
...state.user,
skills: [...state.user.skills, action.payload]
}
};
// 或者直接使用深拷贝
case 'DEEP_UPDATE':
return _.cloneDeep(processUpdate(state, action));
}
}
let user = {name: 'John'};
let team = {leader: user};
user.team = team;
// JSON方法会报错
// let badCopy = JSON.parse(JSON.stringify(user));
// 使用WeakMap解决循环引用
function deepClone(obj, hash = new WeakMap()) {
// 实现同上
}
let specialObj = {
date: new Date(),
fn: function() {},
sym: Symbol('foo'),
undef: undefined
};
// JSON方法会丢失这些特殊类型
let badCopy = JSON.parse(JSON.stringify(specialObj));
// {date: "2023-05-20T03:24:00.000Z"}
// 使用lodash或自定义深拷贝可以正确处理
let goodCopy = _.cloneDeep(specialObj);
对于大型对象,深拷贝可能很耗性能。可以考虑:
按需拷贝(只深拷贝需要修改的部分)
使用不可变数据结构(如Immutable.js)
避免频繁深拷贝
React推崇不可变性,通常使用浅拷贝:
// 更新嵌套状态
setUser(prev => ({
...prev,
profile: {
...prev.profile,
skills: [...prev.profile.skills, 'TypeScript']
}
}));
Vue的reactive系统会自动处理响应式数据的变更:
const user = reactive({
name: 'John',
skills: ['JavaScript']
});
// 需要替换整个数组才能触发响应
user.skills = [...user.skills, 'Vue'];
浅拷贝适用于简单对象或性能敏感场景,常用...
、Object.assign()
等方法
深拷贝适用于复杂嵌套对象,常用JSON方法
、lodash.cloneDeep
或自定义实现
现代前端框架通常结合浅拷贝和不可变原则来管理状态
注意处理循环引用和特殊类型等边界情况
掌握深浅拷贝的区别和适用场景,能够帮助开发者写出更健壮、可维护的前端代码。在实际开发中,应根据数据结构和需求选择合适的拷贝方式。