题目一 实现浅拷贝
js库提供的方法基本是浅拷贝,Object.assign、扩展运算符、数组slice、concat,如果里面的元素是对象类型,则是共享的,会相互影响。
手写浅拷贝实现如下:
function shallowCopy(obj) {
if (!obj || typeof obj !== 'object') {
return;
}
let result = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = obj[key];
}
}
return result;
}
题目二 实现深拷贝
拷贝的对象值之间不能相互影响。实现深拷贝的方式有:
1. JSON.stringify
但有如下问题:
- 不能拷贝function、symbol、undefined类型, 执行后都会消失,如果值在数组中,则变为null
- 如果有循环引用会直接报错
- date类型会变为字符串
- 除数组外属性顺序不保证
2. 手写深拷贝
- map处理循环引用,存储已经cp过的对象,如果处理过直接返回,否则会无限递归,结果对应的循环属性会变为undefined
- 初始化要判断是对象还是数组
function deepCopy(obj) {
const map = new Map();
const clone = (obj) => {
if (!obj || typeof obj !== 'object') {
return;
}
console.log(map);
if (map.has(obj)) {
return map.get(obj);
}
let result = Array.isArray(obj) ? [] : {};
map.set(obj, result);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
const val = obj[key];
if (typeof val === 'object') {
result[key] = clone(val);
} else {
result[key] = val;
}
}
}
return result;
}
return clone(obj);
}
var obj1 = {a: 1, b:2, c: [1, 2], d: {a: 1, b: 1}};
obj1.e = obj1;
deepCopy(obj1);
题目三 手写Object.assign
- 过滤空对象
- 只拷贝自身属性
- 过滤undefined属性
function myAssign(target, ...source) {
if (target === null) {
throw new TypeError('not null or undefined');
}
source.forEach(obj => {
if (!obj) {
return;
}
for (let key in obj) {
if (obj.hasOwnProperty(key) && obj[key] !== undefined) {
target[key] = obj[key];
}
}
});
return target;
}
题目四 手写extend实现深拷贝
这个其实和assign类似,但是本次我们实现深拷贝,和上面题目三注意点都要有:
- 类型判断
- 循环引用
- 过滤空对象,过滤undefined属性
function extend(target, ...source) {
if (typeof target !== 'object') {
return;
}
source.forEach(obj => {
if (!obj) {
return;
}
for (let key in obj) {
const sourceVal = obj[key];
if (sourceVal === undefined) {
return;
}
if (typeof sourceVal !== 'object') {
target[key] = sourceVal;
} else {
// 处理a.b = a的循环引用情况
if (sourceVal === target[key]) {
continue;
}
target[key] = Array.isArray(sourceVal) ? [] : {};
target[key] = extend(target[key], sourceVal);
}
}
});
return target;
}
var a = {
a:1,
b: 2
}
var b = {
c: [1, 2],
d: {a: 'a'},
e: 3
}
// 形成自身循环引用
b.f = b;
c = extend(a, b);
console.log(c)
b.c.push(3)
console.log(b.c, c.c)
题目五 判断两个对象相等
function equal(obj1, obj2) {
if (obj1 === obj2) {
return true;
}
if (Object.keys(obj1).length !== Object.keys(obj2).length) {
return false;
}
for (let key in obj1) {
if (!obj1.hasOwnProperty(key)) {
continue;
}
if (!obj2.hasOwnProperty(key)) {
return false;
}
const val = obj1[key];
if (typeof val !== 'object') {
if (val !== obj2[key]) {
return false;
}
} else if (!equal(val, obj2[key])){
return false;
}
}
return true
}
相关知识点
说到循环引用处理,就想到
- node和es6的模块引用,对循环引用的处理是不一样的。
- js中垃圾回收机制:引用计数与标记清除
引用计数: