一. 数据类型存储
1. 在js中 有基本类型及引用类型;
基本类型数据保存在栈内存中;
引用类型数据保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中。
二. 浅拷贝
浅拷贝: 就是创建新数据且这个数据有着原始数据属性值的一份精确拷贝;
如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址。
即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址;
const shallowClone = (obj: any) => {
const newObj: any = {};
for (const prop in obj) {
// eslint-disable-next-line no-prototype-builtins
if (obj.hasOwnProperty(prop)) {
newObj[prop] = obj[prop];
}
}
return newObj;
};
在js中 浅拷贝现象为 Object.assign Array.prototype.slice() 使用拓展运算符实现的复制
1.Object.assign
const test = () => {
const obj: any = {
age: 20,
hobby: ['aaa', 'bbb'],
names: {
name1: '1',
name2: '2',
},
love: function () {
console.log('11111111');
},
};
const newObj: any = Object.assign({}, obj);
};
2. slice()
const demoArr: any = ['11', '22', '33'];
const tempArr: any = demoArr.slice(0);
tempArr[1] = '44';
console.log(demoArr); // ['11', '22', '33']
console.log(tempArr); // ['11', '44', '33']
3. concat()
const demoArr: any = ['11', '22', '33'];
const tempArr: any = demoArr.concat();
tempArr[1] = '44';
console.log(demoArr); // ['11', '22', '33']
console.log(tempArr); // ['11', '44', '33']
4,拓展运算符
const demoArr: any = ['11', '22', '33'];
const tempArr: any = [...demoArr];
tempArr[1] = '44';
console.log(demoArr); // ['11', '22', '33']
console.log(tempArr); // ['11', '44', '33']
三. 深拷贝
深拷贝开辟一个新的栈 两个对象属性完全相同 但是对应的两个不同的地址 修改一个对象的属性不会改变另一个对象的属性。
常见深拷贝方式:_.cloneDeep() jQuery.extend() JSON.stringify() 及手写循环递归;
1._.cloneDeep()
import _ from 'lodash';
const obj: any = {
a: 1,
b: {
f: { g: 1 },
say: function () {
console.log('111111');
},
},
c: [1, 2, 3],
};
const obj2: any = _.cloneDeep(obj);
2. jQuery.extend()
const $ = require('jquery');
const obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
const obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false
3.JSON.stringify()
const obj2=JSON.parse(JSON.stringify(obj1));
但是这种方式会存在弊端 会忽略 undefined symbol和函数
const obj: any = {
name: '1',
age: undefined,
demoFun: function () {
console.log('demoFun');
},
sex: Symbol('male'),
};
const tempObj = JSON.parse(JSON.stringify(obj));
console.log(tempObj, 'tempObj'); // {name: '1'}
4. 循环递归
const deepClone = (obj: any, hash = new WeakMap()) => {
if (obj === null || obj === undefined) return obj; // 如果是null 或者undefined 就不进行拷贝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== 'object') return obj;
if (hash.get(obj)) return hash.get(obj);
const cloneObj = new obj.constructor(); // 所属类原型上的constructor 原型上的constructor指向当前本身类
hash.set(obj, cloneObj);
for (const key in obj) {
// eslint-disable-next-line no-prototype-builtins
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
};
浅拷贝和深拷贝都创建出一个新的对象 但在复制对象属性的时候 行为是不一样的
浅拷贝只复制属性指向某个对象的指针,而不是复制对象的本身,新旧对象还是共享一块内存,修改对象属性会影响原对象;
const obj: any = {
name: 'init',
arr: [1, [2, 3], 4],
};
const shallowObj = shallowClone(obj);
shallowObj.name = 'update';
shallowObj.arr[1] = [5, 6, 7];
console.log(obj, 'obj'); // {name: 'init', arr: [1, [5, 6, 7], 4]}
console.log(shallowObj, 'shallowObj'); // {name: 'update', arr: [1, [5, 6, 7], 4]}
但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存 修改新对象不会影响原对象。
const obj: any = {
name: 'init',
arr: [1, [2, 3], 4],
};
const deepObj = deepClone(obj);
deepObj.name = 'update';
deepObj.arr[1] = [5, 6, 7];
console.log(obj, 'obj'); // {name: 'init', arr: [1, [2, 3], 4]}
console.log(deepObj, 'deepObj'); // {name: 'init', arr: [1, [5, 6, 7], 4]}
总结:
拷贝类型为引用类型的情况下:
1.浅拷贝是拷贝一层,属性为对象时 浅拷贝时复制 两个对象指向同一个地址;
2.深拷贝时拷贝深层次 属性为对象时 深拷贝是新开栈 两个对象指向不同的地址;