559 对象和数组的深浅拷贝

数组浅拷贝练习题

let utils = (function () {
  /*
   * toArray:转换为数组的方法
   *   @params
   *      不固定数量,不固定类型
   *   @return
   *      [Array] 返回的处理后的新数组
   * by haha on 2020
   */

  // 方法1
  function toArray(...args) {
    // ES6的剩余运算符获取的参数集合本身就是数组
    return args;
  }

  // 方法2
  function toArray() {
    // arguments是实参集合,获取的结果是一个类数组(箭头函数中没有arguments),不能直接调取数组的办法(因为它不是ARRAY的实例)
    // return [...arguments];
    return Array.from(arguments);
  }

  // 方法3
  function toArray() {
    // 把类数组转换为数组(克隆)
    // 原理:只要把slice执行,让方法中的this变为arguments,这样就可以实现把类数组arguments转换为数组
    // 1.让slice执行: Array.prototype.slice()  或者  [].slice()
    // 2.把this改变成为arguments   [].slice.call(arguments)
    // 前提:操作数组的代码(内置方法中的代码)也需要适配改变的THIS(类数组)

    [].forEach.call(arguments, item => {
      console.log(item);
    });

    return [].slice.call(arguments);

    /* let arr = [];
    for (let i = 0; i < arguments.length; i++) {
      arr.push(arguments[i]);
    }
    return arr; */
  }

  return {
    toArray
  };
})();

let ary = utils.toArray(10, 20, 30); // => [10,20,30]
console.log(ary);

ary = utils.toArray('A', 10, 20, 30); // => ['A',10,20,30]
console.log(ary);


// 简单模拟slice的实现
Array.prototype.slice = function slice() {
  // this -> ary
  let arr = [];
  for (let i = 0; i < this.length; i++) {
    arr.push(this[i]);
  }
  return arr;
};
// let ary = [10, 20, 30, 40];
// ary.slice() 把数组克隆一份
// console.log(ary.slice());

浅拷贝

  • 克隆:内存地址是不一样的

【for循环也可以实现浅拷贝。】

实现对象的克隆:

Object.assign:浅比较/浅克隆

{...obj}:展开运算符,也只能展开第一级,也是浅克隆

newObj = obj :这不是克隆,只是赋值

let obj1 = { a: 1, b: 2, c: [5, 6] }
let obj2 = Object.assign({}, obj1)
console.log(obj2, obj1 == obj2) // {a: 1, b: 2, c: Array(2)}   false
console.log(obj1.c === obj2.c) // true

let obj3 = { ...obj1 }
console.log(obj3) // {a: 1, b: 2, c: Array(2)}
console.log(obj1.c === obj3.c) // true

实现数组的克隆

slice、concat... 这些都是浅克隆

forEach、map、reduce... 遍历也只是遍历第一级

let newArr = arr.concat([]);

console.log(newArr === arr); // => false

console.log(newArr[2] === arr[2]); // => true

// 补充:使用for循环实现对象、数组的浅拷贝
// 使用for循环实现对象的浅拷贝
let obj1 = { a: 1, b: 2, c: [5, 6] }
let newObj = {}

for (let i in obj1) {
    newObj[i] = obj1[i]
}

console.log(newObj) // {a: 1, b: 2, c: Array(2)}
console.log(obj1.c === newObj.c) // true


// ---------------------------


// 使用for循环实现数组的浅拷贝
let arr = [11, 22, {name: '哈哈'}]
let newArr = []

for(let i=0;i<arr.length;i++) {
    newArr[i] = arr[i]
}
console.log(newArr) // [11, 22, {name: '哈哈'}]
console.log(newArr[2] === arr[2]) // true

深拷贝

JSON.parse(JSON.stringify())

// 暴力法:把原始数据直接变为字符串,再把字符串变为对象(此时浏览器要重新开辟所有的内存空间),实现出深度克隆、深拷贝
let arr = [11, 22, { name: '哈哈' }]
let newArr = JSON.parse(JSON.stringify(arr));
console.log(newArr);
console.log(newArr === arr); // => false
console.log(newArr[2] === arr[2]); // => false

// 出现的问题:正则会变为空对象,日期变为字符串,函数、undefined、Symbol直接消失,BigInt直接报错
let newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj);
console.log(newObj === obj); // => false
console.log(newObj.c === obj.c); // => false

自己实现深拷贝 【有缺陷,不用】

/* 
补充:获取实例的构造函数
console.log((11).constructor) // Number函数
console.log(('11').constructor) // String函数
console.log((true).constructor) // Boolean函数
console.log((false).constructor) // Boolean函数
console.log((BigInt(10)).constructor) // BigInt函数
console.log((BigInt('33')).constructor) // BigInt函数
// console.log((undefined).constructor) // 报错
// console.log((null).constructor) // 报错
console.log((function fn() {}).constructor) // Function函数
console.log(({}).constructor) // Object函数
*/


/* 数组或者对象的深克隆/浅克隆 */
let obj = {
  a: 100,
  b: [10, 20, 30],
  c: function () { },
  d: /^\d+$/,
  e: new Date(),
  f: Symbol('f'),
  g: BigInt('10')
};

let arr = [10, [100, 200], {
  x: 10,
  y: 20
}];

// 深克隆、深拷贝
function cloneDeep(obj) {
  // 获取实例的构造函数 【当obj是部分基本数据类型,或者是空对象{}的时候,这样写有问题,应该先用Object(obj)转换】
  const constructor = obj.constructor;

  if (obj === null) return null;
  if (typeof obj !== "object") return obj;
  // 正则实例、日期实例的constructor都是返回RegExp、Date构造函数 --> console.log(new Date().constructor):Date、 console.log(/\d/.constructor):RegExp.
  // constructor.name:返回构造函数的名字:(11).constructor.name --> Number,是constructor.name,不是constructor
  if (/^(RegExp|Date)$/i.test(constructor.name)) return new constructor(obj);

  // 重新创建一个constructor类的实例,用于保存克隆的数据
  // 不能写成 let clone = {},因为这样写就是往对象里克隆,而有时候克隆的是数组,so应该往obj同样的数据类型的实例中克隆
  let clone = new constructor();

  for (let key in obj) {
    if (!obj.hasOwnProperty(key)) break;
    // 如果不递归,只会实现浅拷贝。如果obj[key]是引用类型,需要重新cloneDeep。不管是基本、引用类型,都cloneDeep。
    clone[key] = cloneDeep(obj[key]);
  }
  return clone;
}

let newArr = cloneDeep(arr);
console.log(newArr);
console.log(newArr === arr); // => false
console.log(newArr[2] === arr[2]); // => false 

let newObj = cloneDeep(obj);
console.log(newObj);
console.log(newObj === obj); // => false
console.log(newObj.d === obj.d); // => false

posted on 2020-10-23 20:15  冲啊!  阅读(137)  评论(0)    收藏  举报

导航