前言 js基础类型
- 基本类型:Number、Boolean、String、null、undefined、symbol(ES6 新增的),BigInt(ES2020)
- 引用类型:Object,对象子类型(Array,Function)
1. 浅克隆
克隆与被克隆对象指向同一段堆内存,下例obj与o互相影响
// 浅克隆函数
function shallowClone(o) {
const obj = {};
for ( let i in o) {
obj[i] = o[i];
}
return obj;
}
// 被克隆对象
const oldObj = {
a: 1,
b: [ 'e', 'f', 'g' ],
c: { h: { i: 2 } }
};
const newObj = shallowClone(oldObj);
console.log(newObj.c.h, oldObj.c.h); // { i: 2 } { i: 2 }
console.log(oldObj.c.h === newObj.c.h); // true
使用Object.assign(target, source)浅克隆
const target = { a: 1, b: 2 }
const source = { b: 4, c: 5 }
const returnedTarget = Object.assign(target, source)
target // { a: 1, b: 4, c: 5 }
returnedTarget // { a: 1, b: 4, c: 5 }
赋值(=)与浅拷贝的区别
var obj1 = {
'name' : 'zhangsan',
'age' : '18',
'language' : [1,[2,3],[4,5]],
};
var obj2 = obj1;//赋值得到
var obj3 = shallowCopy(obj1);//浅拷贝得到
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
obj2.name = "lisi";
obj3.age = "20";
obj2.language[1] = ["二","三"];
obj3.language[2] = ["四","五"];
console.log(obj1);
//obj1 = {
// 'name' : 'lisi',
// 'age' : '18',
// 'language' : [1,["二","三"],["四","五"]],
//};
console.log(obj2);
//obj2 = {
// 'name' : 'lisi',
// 'age' : '18',
// 'language' : [1,["二","三"],["四","五"]],
//};
console.log(obj3);
//obj3 = {
// 'name' : 'zhangsan',
// 'age' : '20',
// 'language' : [1,["二","三"],["四","五"]],
//};
浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。
所以修改浅拷贝中的属性age时,不会修改原始数据,但language
属性是一个数组,属于引用类型,浅拷贝不会复制出引用类型的数据。
基本数据类型:undefined,boolean,number,string,null
引用数据类型:object,包括有Array,function
深拷贝:将 B 对象拷贝到 A 对象中,包括 B 里面的子对象,
浅拷贝:将 B 对象拷贝到 A 对象中,但不包括 B 里面的子对象
2. 深克隆
JSON.parse方法
JSON
对象parse
方法可以将JSON
字符串反序列化成JS
对象,stringify
方法可以将JS
对象序列化成JSON
字符串,这两个方法结合起来就能产生一个便捷的深克隆.
const newObj = JSON.parse(JSON.stringify(oldObj));
const oldObj = {
a: 1,
b: [ 'e', 'f', 'g' ],
c: { h: { i: 2 } }
};
//下面就是深克隆 newObj与oldObj互不影响
const newObj = JSON.parse(JSON.stringify(oldObj));
console.log(newObj.c.h, oldObj.c.h); // { i: 2 } { i: 2 }
console.log(oldObj.c.h === newObj.c.h); // false
newObj.c.h.i = 'change';
console.log(newObj.c.h, oldObj.c.h); // { i: 'change' } { i: 2 }
但是它存在缺陷:
- 无法实现对函数、RegExp等特殊对象的克隆
- 会抛弃对象的constructor,所有的构造函数指向Object
- 对象有循环引用,会报错。
JSON.stringfy
1. JSON.stringify 方法会忽略对象的不可遍历属性
enumberable: false
被设置的对应属性将会被忽略
2. JSON.stringify 方法的第二个参数
指定需要转成字符串的属性。
JSON.stringify({ a:1, b:2 }, ['a'])
// '{"a":1}'
更改默认的字符串化的行为。
function f(key, value) {
if (typeof value === "number") {
value = 2 * value;
}
return value;
}
JSON.stringify({ a:1, b:2 }, f)
// '{"a":2,"b":4}'
3. JSON.stringify 方法的第三个参数
- 如果是数字,表示每个属性前面添加的空格(最多不超过10个);
- 如果是字符串(不超过10个字符),则该字符串会添加在每行前面。
深克隆的实现
基础版本,不考虑数组和循环应用etc:
- 如果是原始类型,无需继续拷贝,直接返回
- 如果是引用类型,创建一个新的对象,遍历需要克隆的对象,将需要克隆对象的属性执行深拷贝后依次添加到新对象上。
function clone(target) {
if (typeof target === 'object') {
let cloneTarget = {};
for (const key in target) {
cloneTarget[key] = clone(target[key]);
}
return cloneTarget;
} else {
return target;
}
};
优化版:
const clone = parent => {
const parent = []
const children = []
const _clone = parent => {
if (parent === null) return null;
if (typeof parent !== 'object') return parent;
let child, proto;
if(isType(parent, 'Array')) {
//对数组做特殊处理
child = []
} else if (isType(parent, 'RegExp')) {
//对正则对象特殊处理
child = new RegExp(parent.source, getRegExp(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (isType(parent, 'Date')) {
// 对Date对象做特殊处理
child = new Date(parent.getTime());
} else {
//处理对象类型
proto = Object.getPrototypeOf(parent);
//利用Object.create切断原型链
child = Object.create(proto)
}
//处理循环引用
const index = parents.indexOf(parent)
if (index !== -1) {
return children[index]
}
parents.push(parent)
children.push(child)
for (let i in parent) {
child[i] = _clone(parent[i])
}
return child;
}
return _clone(parent)
}