Skip to content

JavaScript中深拷贝和浅拷贝(新增浅比较) #9

@qppq54s

Description

@qppq54s

为什么会有浅拷贝和深拷贝

在js里,将对象赋值给一个新的变量:

var a = {a: 1, b: 2};
var b = a;
a===b; // true

因为a和b指向同一个内存地址,所以它们完全相同,而且改变其中一个,另一个也会改变。

在一些情境下,我们需要拷贝一个全新的对象,防止对原对象产生影响。

浅拷贝

常见的浅拷贝方法

$.extend({}, obj);
Array.prototype.slice();
Object.assign(); // let b = {...a}也一样
function shallowCopy(src) {
  var dst = {};
  for(var prop in src) {
    if (src.hasOwnProperty(prop)) {
      dst[prop] = src[prop];
    }
  }
  return dst;
}

可以看出来,浅拷贝就是对象的第一层key-value的复制,对于value值为简单数据类型的可以做到与原对象互不影响。
如果是复杂的对象,浅拷贝还是不能解决之前提到的问题,这时候就需要深拷贝

深拷贝

常见的深拷贝方法

function deepClone(source){
    // 先判断是否为对象
    if(!source || typeof source !== 'object'){
      throw new Error('请传入参数对象');
    }
    // 创建空的对象和数组
    let targetObj = Array.isArray(source) ? [] : {};
    // 如果value是简单数据类型,把对象的key-value拷贝到新的对象
    // 如果value是对象, 递归调用方法,把对象最终解析到基本数据类型
    for (let keys of Object.keys(source)) {
        if(source[keys] && typeof source[keys] === 'object'){
            targetObj[keys] = deepClone(source[keys]);
          }else{
            targetObj[keys] = source[keys];
          }
    }
    return targetObj;
 }

function deepClone(source){
  return JSON.parse(JSON.stringify(source));
}

相对的,既然有深/浅拷贝,就会有深/浅比较

严格/浅比较 (strictEqual / shallowEqual)

严格比较

严格比较由JavaScript语言本身提供 ===

function strictEqual(a, b) {
  return a === b
}

浅比较

这里我们引用react-redux里的源码

const hasOwn = Object.prototype.hasOwnProperty

// is方法可以判断基本数据类型是否相等
function is(x, y) {
  if (x === y) {
    return x !== 0 || y !== 0 || 1 / x === 1 / y    // 排除 -0 === 0 返回 true的情况
  } else {
    return x !== x && y !== y    // 排除NaN === NaN 返回 false的情况
  }
}

// 浅比较(看上去一样)
export default function shallowEqual(objA, objB) {
  // 首先对基本数据类型做比较
  if (is(objA, objB)) return true

  // 基本类型比较完成后,判断参数是否为对象,如果不是直接返回false,因为typeof null是object但null
     的情况在is方法里可以判断,所以也直接返回false
  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {
    return false
  }

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

  // 判断两个对象是否相同,可以先判断key的多少,不一样的肯定不同
  if (keysA.length !== keysB.length) return false

  // 递归判断每一层的key-value,此处与深拷贝类似
  for (let i = 0; i < keysA.length; i++) {
    if (!hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
      return false
    }
  }

  return true
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions