您现在的位置是:网站首页 > 对象拷贝与比较文章详情

对象拷贝与比较

浅拷贝与深拷贝

JavaScript中对象拷贝分为浅拷贝和深拷贝两种方式。浅拷贝只复制对象的第一层属性,而深拷贝会递归复制对象的所有层级。

浅拷贝的常见实现方式:

const obj = { a: 1, b: { c: 2 } };

// 1. Object.assign
const shallowCopy1 = Object.assign({}, obj);

// 2. 展开运算符
const shallowCopy2 = { ...obj };

// 3. 数组的slice方法
const arr = [1, 2, { a: 3 }];
const shallowCopy3 = arr.slice();

深拷贝的实现方式:

// 1. JSON方法(有局限性,不能处理函数和循环引用)
const deepCopy1 = JSON.parse(JSON.stringify(obj));

// 2. 递归实现
function deepClone(source) {
  if (source === null || typeof source !== 'object') {
    return source;
  }
  
  const target = Array.isArray(source) ? [] : {};
  for (let key in source) {
    if (source.hasOwnProperty(key)) {
      target[key] = deepClone(source[key]);
    }
  }
  return target;
}

// 3. 使用第三方库如lodash的_.cloneDeep
const deepCopy3 = _.cloneDeep(obj);

对象比较的几种方式

JavaScript中比较对象有几种不同的方法,每种方法都有其适用场景。

严格相等比较

const obj1 = { a: 1 };
const obj2 = { a: 1 };
const obj3 = obj1;

console.log(obj1 === obj2); // false
console.log(obj1 === obj3); // true

浅比较

只比较第一层属性:

function shallowEqual(objA, objB) {
  if (objA === objB) {
    return true;
  }

  if (typeof objA !== 'object' || objA === null ||
      typeof objB !== 'object' || objB === null) {
    return false;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  for (let i = 0; i < keysA.length; i++) {
    if (objA[keysA[i]] !== objB[keysA[i]]) {
      return false;
    }
  }

  return true;
}

深比较

递归比较所有属性:

function deepEqual(objA, objB) {
  if (objA === objB) {
    return true;
  }

  if (typeof objA !== 'object' || objA === null ||
      typeof objB !== 'object' || objB === null) {
    return false;
  }

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) {
    return false;
  }

  for (let key of keysA) {
    if (!keysB.includes(key) || !deepEqual(objA[key], objB[key])) {
      return false;
    }
  }

  return true;
}

特殊场景处理

循环引用处理

function deepCloneWithCircular(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  
  if (hash.has(obj)) {
    return hash.get(obj);
  }
  
  const cloneObj = Array.isArray(obj) ? [] : {};
  hash.set(obj, cloneObj);
  
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepCloneWithCircular(obj[key], hash);
    }
  }
  
  return cloneObj;
}

const obj = { a: 1 };
obj.self = obj;
const cloned = deepCloneWithCircular(obj);

函数和特殊对象的拷贝

function cloneFunction(func) {
  const body = func.toString();
  if (body.match(/^function/)) {
    return new Function('return ' + body)();
  } else {
    return new Function('return function ' + body)();
  }
}

function cloneRegExp(regexp) {
  const flags = '';
  flags += regexp.global ? 'g' : '';
  flags += regexp.ignoreCase ? 'i' : '';
  flags += regexp.multiline ? 'm' : '';
  return new RegExp(regexp.source, flags);
}

性能考量

不同拷贝方式的性能差异:

// 性能测试
const largeObj = {};
for (let i = 0; i < 10000; i++) {
  largeObj['key' + i] = { value: Math.random() };
}

console.time('JSON方法');
const copy1 = JSON.parse(JSON.stringify(largeObj));
console.timeEnd('JSON方法');

console.time('递归深拷贝');
const copy2 = deepClone(largeObj);
console.timeEnd('递归深拷贝');

console.time('lodash深拷贝');
const copy3 = _.cloneDeep(largeObj);
console.timeEnd('lodash深拷贝');

实际应用场景

React中的浅比较

React的PureComponent和React.memo使用浅比较来优化性能:

class MyComponent extends React.PureComponent {
  render() {
    return <div>{this.props.value}</div>;
  }
}

// 函数组件
const MemoizedComponent = React.memo(function MyComponent(props) {
  return <div>{props.value}</div>;
});

Redux中的不可变更新

Redux要求状态不可变,需要使用正确的拷贝方式:

// 错误的方式 - 直接修改
state.someProperty.someValue = newValue;

// 正确的方式 - 创建新对象
return {
  ...state,
  someProperty: {
    ...state.someProperty,
    someValue: newValue
  }
};

深度比较的应用

在复杂状态比较时可能需要深度比较:

// 自定义比较函数
const areEqual = (prevProps, nextProps) => {
  return deepEqual(prevProps.data, nextProps.data);
};

const MemoizedDataComponent = React.memo(DataComponent, areEqual);

浏览器API中的拷贝

现代浏览器提供了一些新的拷贝API:

// 结构化克隆算法
const original = { a: 1, date: new Date() };
const cloned = structuredClone(original);

// MessageChannel也可以用于深拷贝
function deepCloneUsingMessageChannel(obj) {
  return new Promise(resolve => {
    const { port1, port2 } = new MessageChannel();
    port2.onmessage = ev => resolve(ev.data);
    port1.postMessage(obj);
  });
}

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步