一、数据类型
js分基本数据类型和引用数据类型。
基本数据类型:Number 、String 、Boolean 、Symbol (详解)、undefined 、null,存储在堆当中,动态分配的内存,大小不定也不会自动释放。
引用数据类型:object,栈中存储地址,自动分配内存空间。地址(0xffff) -> 堆中的内容。
二、浅拷贝
对于 基本数据类型 来说,修改拷贝前后互不影响;
对于 引用数据类型来说,修改拷贝前后第一层互不影响,但是 修改深层次内容会有影响,因为拷贝前后的变量指向的是同一块内存空间。
1、方法:
a、Object.assign()方法:(文档)
/*
* Obect.assign(target, ...sources);
* target:目标对象——将源的属性应用到什么,修改后返回。
* sources:源对象——包含您要应用的属性的对象。
*/
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };
const obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
b、展开运算符(...): ( 文档)
let object1={
name:'张三',
age:18,
sex:'男'
};
let object2={ ...object1 } ;
console.log(object2);// { name:'张三', age:18, sex:'男'};
object2.sex='女';
console.log(object2); // {name: '张三', age: 18, sex: '女'}
console.log(object1); // {name: '张三', age: 18, sex: '男'}
c、自己通过js实现
// 通过Object.keys()处理
let data={
pageNum:1,
pageSize:10,
name:'测试',
content:'描述',
age:18,
sex:null,
appId:0,
};
let params={};
Object.keys(data).forEach(key=>{
if(data[key]||data[key]===0){
params[key]=data[key]
}
});
console.log(params);// {
pageNum: 1,
pageSize: 10,
name: "测试",
content: "描述",
age: 18,
appId: 0,
}
2、注意:
a、Object.assign()方法:
合并时有相同属性,较晚来源的属性会覆盖较早的来源;
b、展开运算符(...):
展开语法和 Object.assign() 行为一致,执行的都是浅拷贝 (只遍历一层)
c、js实现:
只遍历一层的情况下可以这样处理,多层的情况下需要用到递归
三、深拷贝
针对引用数据类型多层而言,深拷贝相当于在内存空间内重新开辟了新的空间存储值,所以修改拷贝前后的变量不会互相影响。
1、方法:
a、JSON.stringfy() & JSON.parse()
let object1={
pageNum:1,
pageSize:10,
eq:{
id:1,
},
like:{
name:'张三',
},
orders:[],
};
let object2=JSON.parse(JSON.stringfy(object1));
console.log(object2);// { pageNum:1, pageSize:10,
eq:{
id:1
},
like:{
name:'张三'
},
orders:[]
}
b、递归遍历,一个值一个值的拷贝
/**
* 判断变量的类型
* @param {object} value 变量值
*/
function checkType(value) {
return Object.prototype.toString.call(value).slice(8, -1);
}
/**
* 深拷贝(递归)
* @param {*} sourceValue 需要拷贝的值
*/
function deepClone(sourceValue) {
// 如果传入的数据是简单类型(不是 {} & []),直接返回即可
if (typeof sourceValue !== "object") {
return sourceValue;
}
// 判断 传入参数的数据类型(object or array)
let targetType = checkType(sourceValue);
// 根据传入参数的数据类型,创建 初始存储结果的变量类型 {} or []
let result = targetType === "Object" ? {} : [];
// 遍历 sourceValue (for...in可以遍历数据和对象)
// 避免数组内有自定义属性,遍历数组使用 for...of,遍历对象 for...in
if (targetType === "Array") {
// 传入参数是数组时,次数使用的是 for...of 遍历,当然,也可以使用 数组的其他遍历方法
for (const [key, value] of sourceValue.entries()) {
let itemType = checkType(item);
// 如果 value 是 数组 或 对象,则继续遍历
if (itemType === "Object" || itemType === "Array") {
result[key] = deepClone(value);
} else {
// 如果 value 是 基本数据类型 或者 函数,直接赋值即可
result[key] = value;
}
}
} else {
// 传入参数是对象时
for (const key in sourceValue) {
// 遍历数组时,key 为数组的 下标
// 遍历对象时,key 为对象的 key
// hasOwnProperty 只能检验对象自身的属性,不能检验继承属性,也不能检验原型链上的属性
if (sourceValue.hasOwnProperty(key)) {
const item = sourceValue[key];
let itemType = checkType(item);
// 如果 value 是 数组 或 对象,则继续遍历
if (itemType === "Object" || itemType === "Array") {
result[key] = deepClone(item);
} else {
// 如果 value 是 基本数据类型 或者 函数,直接赋值即可
result[key] = item;
}
}
}
}
// 返回 result 即可
return result;
}
c、loadsh.cloneDeep(value)
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
2、注意:
a、JSON.parse(JSON.stringfy(value)):
(1)、如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象;
(2)、如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象
(3)、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
(4)、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null;
b、递归遍历:
注意判断数据的类型
四、总结
基本数据类型可以用浅拷贝,引用类型数据使用时用深拷贝。根据需求情况合理使用深拷贝的方法,个人建议使用loadsh.cloneDeep()方法