【前端】JavaScript--浅拷贝与深拷贝
浅拷贝
复制基本类型和值
var a = 1;
b = a;
a = 2;
console.log(a, b);
2 1
深拷贝
对引用类型拷贝,会进行地址的拷贝
var a = { c: 1 };
b = a;
a.c = 2;
console.log(a.c, b.c);
2 2
浅拷贝的实现
function shallowClone(source) {var target = {};for (var i in source) {if (source.hasObject(i)) {target[i] == source[i];}}return target;
}
上述代码相关:for in, Object.keys和Object.getOwnPropertyNames的区别
const linglingkwong = Object.create({ age: 30 });
Object.defineProperty(linglingkwong, 'sex', { value: 'female', enumerable: false });
linglingkwong[Symbol('partner')] = 'orm'; // symbol 用来表示唯一的、不可变的值
linglingkwong.own = 1;// for-in
for (const k in linglingkwong)console.log(k) // own age -- 只枚举// Object.keys
console.log(Object.keys(linglingkwong)); // ['own'] -- 自身,可枚举// Object.getOwnPropertyNames
console.log(Object.getOwnPropertyNames(linglingkwong)); // ['sex', 'own']
为什么会出现不同的结果?
上述对象 linglingkwong
中有三种属性
- 原型链
- 不可枚举
- Symbol
如何区分这三种属性呢?
JavaScript 中的 Object.create() 方法用于创建一个新对象,并指定该对象的原型。
即,age 是 linglingkwong 的原型。
const linglingkwong = Object.create({ age: 30 });
创建了一个空对象,它的原型指向 {age : 30}
{age : 30}
创建了一个新的匿名对象,它有一个属性age,值为30。
Object.create(...)
以这个匿名对象为原型,创建了一个新对象。
const linglingkwong = ...
将这个新对象赋值给 linglingkwong
即 age 为原型。
enumerable:true时,为可枚举。它是描述内部属性的。
Symbol是对象属性的属性,它表示该属性是唯一的。
由上述结果可知:
三种方式种只有 for-in
可以访问原型链。
当内部属性为不可枚举时,不能用for-in
遍历到,也不能用Object.keys
访问。
上述三种方法中,只有Object.getOwnPropertySymbols(obj)
可以访问Symbol属性。
深拷贝的实现
弱点:存在爆栈问题,循环引用问题
function clone(source) {var target = {};for (var i in source) {if (source.hasOwnProperty(i)) {if (typeof source[i] === 'object') {target[i] = clone(source[i]);} else {target[i] = source[i];}}}return target;
}
不过,上述代码中仍存在一些问题。
没有对参数做检验
函数需要校验参数,不是对象的话直接返回。
if (!isObject(source)) return source;
不过上述判断是否为对象的逻辑不严谨,如果遇到null,会误判成object。
因此,采用:
if (source == null || !isObject(source)) return source;
解决循环引用的问题
if (hash.has(source)) return hash.get(source);
没有考虑数组的兼容
需要区分数组和普通对象
const target = Array.isArray(source) ? [] : {};
hash.set (source, target);
for (const key of Reflect.ownKeys(source)){target[key] = clone(source[key], hash);
}
一行代码的深拷贝
弱点:存在循环引用问题,爆栈问题
function cloneJSON(source) {return JSON.parse(JSON.stringify(source));
}
破解循环引用
// 遍历uniqueList
function find(arr, item) {for (let i = 0; i < arr.length; i++) {if (arr[i].source === item) {return arr[i]}}return null;
}
function cloneForce(x) {const uniqueList = []; // 用来去重let root = {};// 循环数组const loopList = [{parent: root,key: undefined,data: x,}];while (loopList.length) {// 深度优先const code = loopList.pop();const parent = node.parent;const key = node.key;const data = node.data;// 初始化赋值目标 key为undefined则拷贝到父元素,否则拷贝到子元素let res = parent;if (typeof key !== 'undefined') {res = parent[key] = {};}let uniqueData = find(uniqueList, data);// 数据已经存在if (uniqueData) {parent[key] = uniqueData.target;break;}// 数据不存在// 保存数据源 在拷贝数据中对应呃引用uniqueList.push({source: data,target: res,})for (let k in data) {if (data.hasOwnProperty(k)) {if (typeof data[k] === 'object') {// 下一次循环loopList.push({parent: res,key: k,data: data[k],});} else {res[k] = data[k];}}}}return root;
}