创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
默认情况下:引用类型(object)都是浅拷贝
简单理解:对于对象来说,就是对最外层数据的拷贝,如果对象只有一层,则相当于深拷贝,如果对象有多层,即某个属性为引用数据类型,则拷贝的是该引用类型在堆中的地址,不会在内存创建一个新的对象。
用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
- // 对象的浅拷贝
- // 1.Object.assign()
- let obj0={a:0}
- let obj2 = Object.assign(obj0,obj1)
- console.log(obj0===obj2) //true
- console.log(obj1===obj2) // false
- obj1.name = '张三1' //obj2不变
- obj1.hobby[0]='打篮球' //obj2变了
- obj1.friend.name='李四1' //obj2变了
- console.log(obj1)
- console.log(obj2)
-
- // 2.展开运算符
- let obj3 = {...obj1}
- obj1.hobby.push('摄影') //obj3会改变
- console.log(obj3)
如果被拷贝对象的属性值为简单类型(如Number,String),通过Object.assign({},Obj)得到的新对象为深拷贝;如果属性值为对象或其它引用类型,则只拷贝了该引用类型的地址。
将alpha和numeric合并为一个新数组并返回
- let arr1 = ['哈哈',18,{name:'哈利',age:10}]
- let arr2 = ['嘿嘿',19,{name:'赫敏',age:11}]
- // 1.concat
- let arr3 = arr1.concat(arr2)
- console.log(arr3===arr1) //false
- console.log(arr3===arr2) // false
- arr1[0]='haha' //arr3不会改
- arr1[2].age=100 //arr3会改
- console.log(arr3)
slice()
方法返回一个新的数组对象,这一对象是一个由 begin
和 end
决定的原数组的浅拷贝(包括 begin
,不包括end
)。原始数组不会被改变。
- // 1.slice截取数组
- const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
-
- console.log(animals.slice(2));
- // expected output: Array ["camel", "duck", "elephant"]
-
- console.log(animals.slice(2, 4));
- // expected output: Array ["camel", "duck"]
-
- //2.slice浅拷贝
- let arr1 = ['哈哈',18,{name:'哈利',age:10}]
- let arr4 = arr1.slice() //不传参数相当于浅拷贝数组
- console.log(arr4===arr1) //false
- arr1[2].name = '哈利111' //arr4会变
- console.log(arr4)
- // 3.展开运算符
- let arr1 = ['哈哈',18,{name:'哈利',age:10}]
- let arr5 = [...arr1]
- arr1[2].age=5 //arr5会改变
- console.log(arr5)
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
根据拷贝的层级不同可以分为浅拷贝和深拷贝,浅拷贝就是只进行一层拷贝,深拷贝就是无限层级拷贝
- // JSON.parse(JSON.stringify(obj))可以处理:
- // string,number,boolean,null,object,array
- // 会忽略symbol,undefined,function,不能处理bigint,date,error,map,set
- // 不能处理循环引用
- let obj = {
- name: "哈哈",
- age: 18,
- bool: true,
- nu: null,
- un: undefined, //会忽略undefined类型的值
- symbol: Symbol(), //会忽略symbol类型的值
- // big: 15n, Do not know how to serialize a BigInt
- map: new Map(), // 不能处理
- set: new Set(),// 不能处理
- obj: {
- color: "blue",
- age: 13,
- },
- arr: [1, 2, 3, 4],
- date: new Date(),
- err: new Error(),
- fun: function add() {}, //会忽略function类型的值
- };
-
- let obj1 = JSON.parse(JSON.stringify(obj));
封装一个函数来实现:
- // 可继续遍历的数据类型
- const objectTag = "[object Object]";
- const arrayTag = "[object Array]";
- const mapTag = "[object Map]";
- const setTag = "[object Set]";
-
- // 不可继续遍历的数据类型
- const stringTag = "[object String]";
- const numberTag = "[object Number]";
- const booleanTag = "[object Boolean]";
- const symbolTag = "[object Symbol]";
- const dateTag = "[object Date]";
- const functionTag = "[object Function]";
- const errorTag = "[object Error]";
-
- // 需要深度遍历的数据类型
- const deepTag = [setTag, mapTag, arrayTag, objectTag];
-
- // 工具函数-遍历数组,使用while提升性能
- function forEach(arr, cb) {
- const len = arr.length;
- let i = -1;
- while (++i < length) {
- cb(arr[i], i);
- }
- return arr;
- }
-
- // 工具函数-判断是否是引用类型
- function isObject(value) {
- const type = typeof value;
- // 这里忽略了function类型 function类型直接返回
- return value !== null && type === "object";
- }
-
- // 工具函数-获取数据实际类型
- function getType(value) {
- return Object.prototype.toString.call(value);
- }
-
- //工具函数-初始化被克隆的对象
- function getInit(value) {
- const cons = value.constructor;
- return new cons();
- }
- // 工具函数-克隆symbol
- function cloneSymbol(value) {
- return Object(Symbol.prototype.valueOf.call(value));
- }
-
- // 工具函数-克隆不可遍历的类型(这里忽略了对函数和正则的处理)
- function cloneOtherType(value) {
- const type = getType(value);
- const ctor = value.constructor;
- switch (type) {
- case stringTag:
- case numberTag:
- case booleanTag:
- case functionTag:
- case errorTag:
- case dateTag:
- return new ctor(value);
- case symbolTag:
- return cloneSymbol(value);
- default:
- return null;
- }
- }
- // 真正实现深拷贝的函数
- function clone(value, map = new WeakMap()) {
- // 不是引用数据类型直接返回
- if (!isObject(value)) return value;
-
- // 根据数据类型做不同的处理
- let copyValue;
- const typeOfValue = getType(value);
- if (deepTag.includes(typeOfValue)) {
- copyValue = getInit(value);
- } else {
- return cloneOtherType(value);
- }
-
- // 处理循环引用
- if (map.has(value)) return map.get(value);
- map.set(value, copyValue);
-
- // 处理Map
- if (typeOfValue === mapTag) {
- value.forEach((item, key) => {
- copyValue.set(key, clone(item, map));
- });
- return copyValue;
- }
- // 处理set
- if (typeOfValue === setTag) {
- set.forEach((item) => {
- copyValue.add(clone(item, map));
- });
- return copyValue;
- }
- // 处理对象
- if (typeOfValue === objectTag) {
- const keys = value.keys();
- forEach(keys, (item) => {
- copyValue[item] = clone(value[item], map);
- });
- //获取属性值为symbol类型的keys
- const symbolKeys = Object.getOwnPropertySymbols(value);
- forEach(symbolKeys, (item) => {
- copyValue[item] = clone(value[item], map);
- });
- return copyValue;
- }
- // 处理数组
- if (typeOfValue === arrayTag) {
- forEach(value, (item, index) => {
- copyValue[index] = clone(item, map);
- });
- return copyValue;
- }
- }