浅拷贝
Object.assign(target, source1, ..., source)
- 用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
- 对象第1层是深拷贝,第2层是浅拷贝
let s1 = {
s1A: 's1A'
}
let s2 = {
s2A: {
s2Aa: 's2Aa'
}
}
let t = { tA: null }
let res = Object.assign(t, s1, s2);
console.log('res', res); // res { tA: null, s1A: 's1A', s2A: { s2Aa: 's2Aa' } }
console.log('t', t); // t { tA: null, s1A: 's1A', s2A: { s2Aa: 's2Aa' } }
// t 和 res 指向同一个内存地址
res.tA = 't 和 res 指向同一个内存地址';
console.log('res', res); // res { tA: 't 和 res 指向同一个内存地址', s1A: 's1A', s2A: { s2Aa: 's2Aa' } }
console.log('t', t); // t { tA: 't 和 res 指向同一个内存地址', s1A: 's1A', s2A: { s2Aa: 's2Aa' } }s
// 对象第1层是深拷贝,第2层是浅拷贝
res.s1A = '深拷贝';
res.s2A.s2Aa = '浅拷贝'
console.log('res', res); // res { tA: 't 和 res 指向同一个内存地址', s1A: '深拷贝', s2A: { s2Aa: '浅拷贝' } }
console.log('s1', s1); // s1 { s1A: 's1A' }
console.log('s2', s2); // s2 { s2A: { s2Aa: '浅拷贝' } }
Array.prototype.slice(start, end)
- 接收开始和结束下标(不包括)两个非必需参数,返回一个新的对象数组,不改变原数组
- 第1层实现深拷贝,第2层实现浅拷贝
let s = [1, 2, [3], 4]
let t1 = s.slice()
let t2 = s.slice(1, 3)
t1[2][0] = 'aa'
console.log('s', s)
console.log('t1', t1)
console.log('t2', t2)
扩展运算符...
// 对象
let a = {
name: "Jake",
flag: {
title: "better day by day",
time: "2020-05-31"
}
}
let b = {...a};
console.log('b', b); // b { name: 'Jake', flag: { title: 'better day by day', time: '2020-05-31' } }
b.name = 'Mark';
b.flag.title = '浅拷贝';
console.log('a', a); // a { name: 'Jake', flag: { title: '浅拷贝', time: '2020-05-31' } }
console.log('b', b); // b { name: 'Mark', flag: { title: '浅拷贝', time: '2020-05-31' } }
// 数组
let s = [1, 2, [3], 4];
let t1 = [...s];
t1[0] = 'a'
t1[2][0] = 'aa'
console.log('s', s); // s [ 1, 2, [ 'aa' ], 4 ]
console.log('t1', t1); // t1 [ 'a', 2, [ 'aa' ], 4 ]
深拷贝
JSON.parse(JSON.stringify(object))
- 缺点:会忽略undefined、symbol、函数;
- 不能解决循环引用、不能处理正则、new Date());
基础版
// 使用 map 映射记录拷贝了哪些属性,防止循环引用
// 用 WeapMap() 代替 Map(),原因:WeapMap弱引用,方便内存回收
function cloneDeep (target, map = new WeakMap()) {
if (typeof target === 'object') {
let cloneTarget = Array.isArray(target) ? [] : {}
if (map.get(target)) {
return target
}
map.set(target, cloneTarget)
for (let key in target) {
// console.log('key: ' + key, target[key])
cloneTarget[key] = cloneDeep(target[key], map)
}
return cloneTarget
} else {
return target
}
}
升级版
const mapTag = '[object Map]'
const setTag = '[object Set]'
const arrayTag = '[object Array]'
const objectTag = '[object Object]'
const argsTag = '[object Arguments]'
const boolTag = '[object Boolean]'
const dateTag = '[object Date]'
const numberTag = '[object Number]'
const stringTag = '[object String]'
const symbolTag = '[object Symbol]'
const errorTag = '[object Error]'
const regexpTag = '[object RegExp]'
const funcTag = '[object Function]'
const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag]
function forEach(array, iteratee) {
let index = -1
const length = array.length
while (++index < length) {
iteratee(array[index], index)
}
return array
}
function isObject(target) {
const type = typeof target
return target !== null && (type === 'object' || type === 'function')
}
function getType(target) {
return Object.prototype.toString.call(target)
}
function getInit(target) {
const Ctor = target.constructor
return new Ctor()
}
function cloneSymbol(targe) {
return Object(Symbol.prototype.valueOf.call(targe))
}
function cloneReg(targe) {
const reFlags = /\w*$/
const result = new targe.constructor(targe.source, reFlags.exec(targe))
result.lastIndex = targe.lastIndex
return result
}
function cloneFunction(func) {
const bodyReg = /(?<={)(.|\n)+(?=})/m
const paramReg = /(?<=\().+(?=\)\s+{)/
const funcString = func.toString()
if (func.prototype) {
const param = paramReg.exec(funcString)
const body = bodyReg.exec(funcString)
if (body) {
if (param) {
const paramArr = param[0].split(',')
return new Function(...paramArr, body[0])
} else {
return new Function(body[0])
}
} else {
return null
}
} else {
return eval(funcString)
}
}
function cloneOtherType(targe, type) {
const Ctor = targe.constructor
switch (type) {
case boolTag:
case numberTag:
case stringTag:
case errorTag:
case dateTag:
return new Ctor(targe)
case regexpTag:
return cloneReg(targe)
case symbolTag:
return cloneSymbol(targe)
case funcTag:
return cloneFunction(targe)
default:
return null
}
}
function clone(target, map = new WeakMap()) {
// 克隆原始类型
if (!isObject(target)) {
return target
}
// 初始化
const type = getType(target)
let cloneTarget
if (deepTag.includes(type)) {
cloneTarget = getInit(target, type)
} else {
return cloneOtherType(target, type)
}
// 防止循环引用
if (map.get(target)) {
return map.get(target)
}
map.set(target, cloneTarget)
// 克隆set
if (type === setTag) {
target.forEach(value => {
cloneTarget.add(clone(value, map))
})
return cloneTarget
}
// 克隆map
if (type === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, clone(value, map))
})
return cloneTarget
}
// 克隆对象和数组
const keys = type === arrayTag ? undefined : Object.keys(target)
forEach(keys || target, (value, key) => {
if (keys) {
key = value
}
cloneTarget[key] = clone(target[key], map)
})
return cloneTarget
}
module.exports = {
clone
}
深拷贝详解1
深拷贝详解2