浅拷贝
对于复制引用数据类型,只会复制数据最外面一层
function shallowCopy(originObj){
let obj = {}
for(let i in originObj){
obj[i] = originObj[i]
}
return obj
}
被复制对象
let my = {
name:'gromy',
age:18,
study:{
gaozhong:{
wl:'理科',
score:481
},
daxue:{
xuexiao:{
name:'江苏理工',
zhuanye:'计算机'
},
time:'4'
}
}
}
执行
let he = shallowCopy(my)
my.study.gaozhong.score=321
321
he.study.gaozhong.score
321
浅复制只复制了最外面一层,所以一层以上的引用还是同一个,使用Object.assign() 也是跟以上相同实现的浅复制。
深拷贝
使用 JSON.stringtify() 实现的深拷贝
问题
var obj={
name:'gromy',
age:NaN,
say:function(){
console.log('我会说话哦!');
},
id:Symbol('id'),
birthday:new Date('1992/01/01')
};
var obj2=JSON.parse(JSON.stringify(obj));
console.log(obj2);
- undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略
- Date 日期调用了 toJSON() 将其转换为了 string 字符串(Date.toISOString()),因此会被当做字符串处理。
- NaN 和 Infinity 格式的数值及 null 都会被当做 null。
- 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。
- 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
完整实现
实现过程
- 判断源目标的数据类型
- 对不同数据类型进行不同的复制
- 递归调用
深拷贝的痛点-循环引用的问题
一般我们拷贝的过程是一边遍历一边拷贝
就会存在拷贝结束后新的对象中还存在对原来对象的引用var obj = {};
var b = {obj};
obj.b = b
解决这个问题的方案是,在拷贝的过程中,开一个暂存区,存储已经拷贝的引用,如果需要引用之前的内容,去引用新对象的地址。
lodash 对此的解决方案
lodash 是用一个栈去存储所有已经拷贝的引用,每次引用之前去查询是否之前已经存在引用// Check for circular references and return its corresponding clone.
stack || (stack = new Stack)
const stacked = stack.get(value)
if (stacked) {
return stacked
}
stack.set(value, result)
lodash 对于类型的处理非常的全面``javascript /**
Object#toString` result references. */ const argsTag = ‘[object Arguments]’ const arrayTag = ‘[object Array]’ const boolTag = ‘[object Boolean]’ const dateTag = ‘[object Date]’ const errorTag = ‘[object Error]’ const mapTag = ‘[object Map]’ const numberTag = ‘[object Number]’ const objectTag = ‘[object Object]’ const regexpTag = ‘[object RegExp]’ const setTag = ‘[object Set]’ const stringTag = ‘[object String]’ const symbolTag = ‘[object Symbol]’ const weakMapTag = ‘[object WeakMap]’
const arrayBufferTag = ‘[object ArrayBuffer]’ const dataViewTag = ‘[object DataView]’ const float32Tag = ‘[object Float32Array]’ const float64Tag = ‘[object Float64Array]’ const int8Tag = ‘[object Int8Array]’ const int16Tag = ‘[object Int16Array]’ const int32Tag = ‘[object Int32Array]’ const uint8Tag = ‘[object Uint8Array]’ const uint8ClampedTag = ‘[object Uint8ClampedArray]’ const uint16Tag = ‘[object Uint16Array]’ const uint32Tag = ‘[object Uint32Array]’
<a name="OhHip"></a>
#### 简单实现
```javascript
function deepClone(value){
let cache = {}; //暂存已经复制的引用
}
获取引用数据类型
function getTypeName(value){
return Object.prototype.toString.call(value)
}
//判断是否是基础类型的数据
function isPrimitive(value){
return (typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'symbol' ||
typeof value === 'boolean'||
value === undefined ||
value === null ||
value === NaN ||
value === Infinity)
}
根据已有数据创建新对象
function creatByTag(value){
let cont = value.constructor
return new cont()
}
object
if(getTypeName(value) === objectTag){
result = {...value}
Reflect.ownKeys(result).forEach((key)=>{
console.log(result[key])
// if(cache[result[key]]){
// result[key] = cache[result[key]]
// }else{
result[key] = baseClone(result[key])
// cache[result[key]] = result[key]
//}
})
return result
}
Array
//array
if(getTypeName(value) === arrayTag){
result = [...value]
result.forEach((item,i)=>{
result[i] = baseClone(result[i])
})
return result
}
map
//Map
if(getTypeName(value) === mapTag){
result = creatByTag(value)
value.forEach((subValue, key) => {
result.set(key, baseClone(subValue))
})
return result
}
正则
function cloneRegExp(regexp) {
const result = new regexp.constructor(regexp.source, reFlags.exec(regexp))
result.lastIndex = regexp.lastIndex
return result
}
symbol
function cloneSymbol(symbol) {
return Object(Symbol.prototype.valueOf.call(symbol))
}
date
function cloneDate(date) {
let cont = date.constructor
return cont(+date)
}
function
function copyFunc(value){
return eval(`(${value.toString()})`)
}
test
let func = function(){
let a = 2
return function(){
return a++
}
}
let f2 = new Function(func)
f2.prototype == func.prototype //false
包装基础数据类型
'[object Boolean]'
'[object Number]'
'[object Strung]'
function cloneBaseType(value) {
let cont = value.constructor
return cont(value)
}
总代码以及测试
const argsTag = '[object Arguments]'
const arrayTag = '[object Array]'
const boolTag = '[object Boolean]'
const dateTag = '[object Date]'
const errorTag = '[object Error]'
const mapTag = '[object Map]'
const numberTag = '[object Number]'
const objectTag = '[object Object]'
const regexpTag = '[object RegExp]'
const setTag = '[object Set]'
const stringTag = '[object String]'
const symbolTag = '[object Symbol]'
const weakMapTag = '[object WeakMap]'
function deepClone(obj){
let cache = {} //暂存已经复制过的引用
//判断类型
function baseClone(value){
let result
if(isPrimitive(value)){
return value
}
//object
if(getTypeName(value) === objectTag){
result = {...value}
Reflect.ownKeys(result).forEach((key)=>{
console.log(result[key])
// if(cache[result[key]]){
// result[key] = cache[result[key]]
// }else{
result[key] = baseClone(result[key])
// cache[result[key]] = result[key]
//}
})
return result
}
//array
if(getTypeName(value) === arrayTag){
result = [...value]
result.forEach((item,i)=>{
result[i] = baseClone(result[i])
})
return result
}
//Map
if(getTypeName(value) === mapTag){
result = creatByTag(value)
value.forEach((subValue, key) => {
result.set(key, baseClone(subValue))
})
return result
}
}
return baseClone(obj)
}
function getTypeName(value){
return Object.prototype.toString.call(value)
}
//判断是否是基础类型的数据
function isPrimitive(value){
return (typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'symbol' ||
typeof value === 'boolean'||
value === undefined ||
value === null ||
value === NaN ||
value === Infinity)
}
function creatByTag(value){
let cont = value.constructor
return new cont()
}
let obj = {
name:'gromy',
xuexiao:{
gaozhong:{
name:'第一中学',
time:4
}
},
family:[
{husband:{name:'super man'}},
{son:{name:'super baby',age:1}}
]
}
let obj2 = deepClone(obj)
console.log('obj2',obj2)
obj.xuexiao.gaozhong.time = 5
obj.family[1].son.age = 0
console.log('obj2',obj2)
lodash 源码
/** `Object#toString` result references. */
const argsTag = '[object Arguments]'
const arrayTag = '[object Array]'
const boolTag = '[object Boolean]'
const dateTag = '[object Date]'
const errorTag = '[object Error]'
const mapTag = '[object Map]'
const numberTag = '[object Number]'
const objectTag = '[object Object]'
const regexpTag = '[object RegExp]'
const setTag = '[object Set]'
const stringTag = '[object String]'
const symbolTag = '[object Symbol]'
const weakMapTag = '[object WeakMap]'
const arrayBufferTag = '[object ArrayBuffer]'
const dataViewTag = '[object DataView]'
const float32Tag = '[object Float32Array]'
const float64Tag = '[object Float64Array]'
const int8Tag = '[object Int8Array]'
const int16Tag = '[object Int16Array]'
const int32Tag = '[object Int32Array]'
const uint8Tag = '[object Uint8Array]'
const uint8ClampedTag = '[object Uint8ClampedArray]'
const uint16Tag = '[object Uint16Array]'
const uint32Tag = '[object Uint32Array]'
/** Used to identify `toStringTag` values supported by `clone`. */
const cloneableTags = {}
cloneableTags[argsTag] = cloneableTags[arrayTag] =
cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
cloneableTags[boolTag] = cloneableTags[dateTag] =
cloneableTags[float32Tag] = cloneableTags[float64Tag] =
cloneableTags[int8Tag] = cloneableTags[int16Tag] =
cloneableTags[int32Tag] = cloneableTags[mapTag] =
cloneableTags[numberTag] = cloneableTags[objectTag] =
cloneableTags[regexpTag] = cloneableTags[setTag] =
cloneableTags[stringTag] = cloneableTags[symbolTag] =
cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true
cloneableTags[errorTag] = cloneableTags[weakMapTag] = false
/** Used to check objects for own properties. */
const hasOwnProperty = Object.prototype.hasOwnProperty
function initCloneByTag(object, tag, isDeep) {
const Ctor = object.constructor
switch (tag) {
case arrayBufferTag:
return cloneArrayBuffer(object)
case boolTag:
case dateTag:
return new Ctor(+object)
case dataViewTag:
return cloneDataView(object, isDeep)
case float32Tag: case float64Tag:
case int8Tag: case int16Tag: case int32Tag:
case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
return cloneTypedArray(object, isDeep)
case mapTag:
return new Ctor
case numberTag:
case stringTag:
return new Ctor(object)
case regexpTag:
return cloneRegExp(object)
case setTag:
return new Ctor
case symbolTag:
return cloneSymbol(object)
}
}
/**
* Initializes an array clone.
*
* @private
* @param {Array} array The array to clone.
* @returns {Array} Returns the initialized clone.
*/
function initCloneArray(array) {
const { length } = array
const result = new array.constructor(length)
// Add properties assigned by `RegExp#exec`.
if (length && typeof array[0] === 'string' && hasOwnProperty.call(array, 'index')) {
result.index = array.index
result.input = array.input
}
return result
}
/**
* The base implementation of `clone` and `cloneDeep` which tracks
* traversed objects.
*
* @private
* @param {*} value The value to clone.
* @param {number} bitmask The bitmask flags.
* 1 - Deep clone
* 2 - Flatten inherited properties
* 4 - Clone symbols
* @param {Function} [customizer] The function to customize cloning.
* @param {string} [key] The key of `value`.
* @param {Object} [object] The parent object of `value`.
* @param {Object} [stack] Tracks traversed objects and their clone counterparts.
* @returns {*} Returns the cloned value.
*/
function baseClone(value, bitmask, customizer, key, object, stack) {
let result
const isDeep = bitmask & CLONE_DEEP_FLAG
const isFlat = bitmask & CLONE_FLAT_FLAG
const isFull = bitmask & CLONE_SYMBOLS_FLAG
if (customizer) {
result = object ? customizer(value, key, object, stack) : customizer(value)
}
if (result !== undefined) {
return result
}
if (!isObject(value)) {
return value
}
const isArr = Array.isArray(value)
const tag = getTag(value)
if (isArr) {
result = initCloneArray(value)
if (!isDeep) {
return copyArray(value, result)
}
} else {
const isFunc = typeof value === 'function'
if (isBuffer(value)) {
return cloneBuffer(value, isDeep)
}
if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
result = (isFlat || isFunc) ? {} : initCloneObject(value)
if (!isDeep) {
return isFlat
? copySymbolsIn(value, copyObject(value, keysIn(value), result))
: copySymbols(value, Object.assign(result, value))
}
} else {
if (isFunc || !cloneableTags[tag]) {
return object ? value : {}
}
result = initCloneByTag(value, tag, isDeep)
}
}
// Check for circular references and return its corresponding clone.
stack || (stack = new Stack)
const stacked = stack.get(value)
if (stacked) {
return stacked
}
stack.set(value, result)
if (tag == mapTag) {
value.forEach((subValue, key) => {
result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack))
})
return result
}
if (tag == setTag) {
value.forEach((subValue) => {
result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack))
})
return result
}
if (isTypedArray(value)) {
return result
}
const keysFunc = isFull
? (isFlat ? getAllKeysIn : getAllKeys)
: (isFlat ? keysIn : keys)
const props = isArr ? undefined : keysFunc(value)
arrayEach(props || value, (subValue, key) => {
if (props) {
key = subValue
subValue = value[key]
}
// Recursively populate clone (susceptible to call stack limits).
assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack))
})
return result
}
export default baseClone