江门市纤凝网

一次彻底搞懂JavaScript中的引用赋值、浅拷贝和深拷贝

2026-04-01 07:01:01 浏览次数:1
详细信息

JavaScript中的引用赋值、浅拷贝和深拷贝

一、核心概念对比

特性 引用赋值 浅拷贝 深拷贝
定义 变量指向同一内存地址 复制第一层属性,嵌套对象仍共享引用 完全独立的副本,所有层级都复制
修改原对象 影响新变量 第一层属性不影响,嵌套对象影响 完全不影响
内存地址 相同 不同(但嵌套对象相同) 完全不同
适用场景 需要共享数据 简单对象,无嵌套或嵌套不变 复杂嵌套对象,需要完全独立

二、引用赋值 (Reference Assignment)

// 引用赋值示例
let obj1 = { name: 'Alice', hobbies: ['reading', 'coding'] };
let obj2 = obj1; // 引用赋值

obj2.name = 'Bob';
console.log(obj1.name); // 'Bob' - 原对象也被修改

obj2.hobbies.push('gaming');
console.log(obj1.hobbies); // ['reading', 'coding', 'gaming'] - 嵌套对象也被修改

console.log(obj1 === obj2); // true - 指向同一个内存地址

关键特点:

三、浅拷贝 (Shallow Copy)

// 浅拷贝的几种方法

// 1. 扩展运算符
let original = { a: 1, b: { nested: 2 } };
let shallowCopy1 = { ...original };

// 2. Object.assign()
let shallowCopy2 = Object.assign({}, original);

// 3. 数组的浅拷贝方法
let arr = [1, 2, { nested: 3 }];
let arrCopy1 = arr.slice();
let arrCopy2 = arr.concat();
let arrCopy3 = [...arr];

// 浅拷贝的特点
original.a = 100;
console.log(shallowCopy1.a); // 1 - 第一层属性不受影响

original.b.nested = 200;
console.log(shallowCopy1.b.nested); // 200 - 嵌套对象受影响(共享引用)

console.log(original === shallowCopy1); // false
console.log(original.b === shallowCopy1.b); // true - 嵌套对象引用相同

四、深拷贝 (Deep Copy)

1. 手动实现深拷贝

// 递归实现深拷贝
function deepClone(obj, hash = new WeakMap()) {
  // 处理基本类型和null
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  // 处理循环引用
  if (hash.has(obj)) {
    return hash.get(obj);
  }

  // 处理日期
  if (obj instanceof Date) {
    return new Date(obj);
  }

  // 处理正则表达式
  if (obj instanceof RegExp) {
    return new RegExp(obj);
  }

  // 处理数组
  if (Array.isArray(obj)) {
    const cloneArr = [];
    hash.set(obj, cloneArr);
    obj.forEach(item => {
      cloneArr.push(deepClone(item, hash));
    });
    return cloneArr;
  }

  // 处理对象
  const cloneObj = Object.create(Object.getPrototypeOf(obj));
  hash.set(obj, cloneObj);

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }

  return cloneObj;
}

2. 使用JSON方法(有限制)

let original = {
  name: 'Alice',
  age: 25,
  hobbies: ['reading', 'coding'],
  address: { city: 'Beijing' }
};

let deepCopy = JSON.parse(JSON.stringify(original));

original.address.city = 'Shanghai';
console.log(deepCopy.address.city); // 'Beijing' - 不受影响

// JSON方法的限制:
let problemObj = {
  date: new Date(), // 会被转换为字符串
  func: function() {}, // 会被忽略
  undefined: undefined, // 会被忽略
  infinity: Infinity, // 会被转换为null
  regex: /pattern/g, // 会变成空对象
  symbol: Symbol('foo'), // 会被忽略
  bigint: BigInt(123) // 会报错
};

let jsonCopy = JSON.parse(JSON.stringify(problemObj));
console.log(jsonCopy);

3. 使用第三方库

// Lodash的_.cloneDeep()
// const _ = require('lodash');
// let deepCopy = _.cloneDeep(original);

// Structured Clone API(现代浏览器/Node.js)
let original2 = { a: 1, b: { nested: 2 } };
let structuredCloneCopy = structuredClone(original2);

五、不同数据类型的处理

// 测试各种数据类型的拷贝行为
const testData = {
  // 基本类型 - 所有拷贝方式都安全
  string: 'hello',
  number: 123,
  boolean: true,
  null: null,
  undefined: undefined,
  symbol: Symbol('test'),
  bigint: BigInt(123),

  // 引用类型 - 需要注意
  array: [1, 2, 3],
  object: { a: 1, b: 2 },
  date: new Date(),
  regex: /pattern/g,
  set: new Set([1, 2, 3]),
  map: new Map([['key', 'value']]),
  function: function() { return 'test'; },

  // 特殊引用
  circularRef: null, // 循环引用
  typedArray: new Uint8Array([1, 2, 3])
};

// 创建循环引用
testData.circularRef = testData;

六、性能比较

// 性能测试函数
function performanceTest(method, data, iterations = 10000) {
  const start = performance.now();

  for (let i = 0; i < iterations; i++) {
    method(data);
  }

  const end = performance.now();
  return end - start;
}

const testObj = { /* 复杂嵌套对象 */ };

// 测试不同方法的性能
console.log('JSON方法:', performanceTest(JSON.parse.bind(null, JSON.stringify(testObj)), testObj));
console.log('递归深拷贝:', performanceTest(deepClone, testObj));
console.log('浅拷贝:', performanceTest(obj => ({ ...obj }), testObj));

七、实战应用场景

场景1:状态管理(React/Vue)

// React中错误的做法 - 引用赋值
const [state, setState] = useState({ user: { name: 'Alice' } });

const updateUser = () => {
  const newState = state; // 错误!引用赋值
  newState.user.name = 'Bob';
  setState(newState); // 不会触发重新渲染
};

// 正确的做法 - 浅拷贝或深拷贝
const updateUserCorrectly = () => {
  // 浅拷贝(适合简单对象)
  setState({ ...state, user: { ...state.user, name: 'Bob' } });

  // 或者使用深拷贝
  // setState(JSON.parse(JSON.stringify(state)));
};

场景2:函数参数处理

// 避免副作用
function processData(data) {
  // 创建副本,避免修改原数据
  const dataCopy = deepClone(data);
  // 对dataCopy进行操作...
  return dataCopy;
}

场景3:缓存/撤销功能

class HistoryManager {
  constructor(initialState) {
    this.history = [deepClone(initialState)];
    this.currentIndex = 0;
  }

  pushState(state) {
    // 保存状态的深拷贝
    this.history = this.history.slice(0, this.currentIndex + 1);
    this.history.push(deepClone(state));
    this.currentIndex++;
  }

  undo() {
    if (this.currentIndex > 0) {
      this.currentIndex--;
      return deepClone(this.history[this.currentIndex]);
    }
    return null;
  }
}

八、选择策略指南

什么时候用引用赋值?

什么时候用浅拷贝?

什么时候用深拷贝?

九、最佳实践

优先使用浅拷贝:除非确需深拷贝,否则使用浅拷贝更高效 使用const声明引用类型:防止意外重新赋值 考虑使用不可变数据结构:如Immutable.js 注意循环引用:深拷贝时要处理循环引用 性能优化:对于大数据量,考虑增量更新

十、总结

理解JavaScript中的拷贝机制是成为高级开发者的关键:

根据具体场景选择合适的拷贝策略,平衡性能与数据安全性的需求。

相关推荐