JSON
在 JSON 中,值必须是以下数据类型之一:
字符串
数字
对象(JSON 对象)
数组
布尔
Null
JSON 的值不可以是以下数据类型之一:
函数
日期
undefined
这种方式有弊端,只能解析JSON数据,会丢失其他类型的数据。
JSON.parse(JSON.strigify(value))
JSON.stringify({a: () => {}}) === '{}' // true
jQuery.extend
这是jQuery.extend源代码,如果出现循环引用的问题,会直接报栈溢出错误 Uncaught RangeError: Maximum call stack size exceeded
jQuery.extend = jQuery.fn.extend = function () {
var options,
name,
src,
copy,
copyIsArray,
clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false
// Handle a deep copy situation
if (typeof target === 'boolean') {
deep = target
target = arguments[1] || {}
// skip the boolean and the target
i = 2
}
// Handle case when target is a string or something (possible in deep copy)
if (typeof target !== 'object' && !jQuery.isfunction(target)) {
target = {}
}
// extend jQuery itself if only one argument is passed
if (length === i) {
target = this
--i
}
for (; i < length; i++) {
// Only deal with non-null/undefined values
if ((options = arguments[i]) != null) {
// Extend the base object
for (name in options) {
src = target[name]
copy = options[name]
// Prevent never-ending loop
if (target === copy) {
continue
}
// Recurse if we're merging plain objects or arrays
if (
deep &&
copy &&
(jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))
) {
if (copyIsArray) {
copyIsArray = false
clone = src && jQuery.isArray(src) ? src : []
} else {
clone = src && jQuery.isPlainObject(src) ? src : {}
}
// Never move original objects, clone them
target[name] = jQuery.extend(deep, clone, copy)
// Don't bring in undefined values
} else if (copy !== undefined) {
target[name] = copy
}
}
}
}
// Return the modified object
return target
}
有以下两个注意点
- 注意循环引用
数组存储对象值,避免循环引用的问题。 - 除了对象和数组外的其他
typeof
为object
的数据类型处理
下面是我的实现,不过对其他数据类型处理不完整,做的比较好的是lodash,这是他的源码
function deepCloneWrap (value) {
let mapCache = []
function deepClone (value) {
if(value instanceof Date) return new Date(value);
if(value instanceof RegExp) return new RegExp(value);
// 这里还应该处理好多其他对象类型的数据
if (typeof value !== 'object') return value
// 如果和之前保存的引用一致则直接返回,则直接返回,避免循环溢出
if (mapCache.length) {
for (let i = 0,len = arr.length; i < len; ++i) {
if (mapCache[i][0] === value) {
return mapCache[i][1]
}
}
}
// 直接取constructor,就不用判断是数组还是对象。
let cloneVal = new value.constructor()
// 将要被克隆的值先存储起来,后面每次对象进入函数先进行比较
mapCache.push([value, cloneVal])
for (let k in value) {
cloneVal[k] = deepClone(value[k])
}
return cloneVal
}
return deepClone(value)
}
let a = {b:{c:1}, date: new Date()}
a.x = a
let newA = deepCloneWrap(a)
console.log(newA)
用WeakMap,一方面是可以通过Map实例的has方法判断是否以及有过当初处理对象,另一方面也可以避免内存泄露。
function deepClone (value, hash = new WeakMap()) {
if(value instanceof Date) return new Date(value);
if(value instanceof RegExp) return new RegExp(value);
if (typeof value !== 'object') return value
if (hash.get(value)) return hash.get(value)
let cloneVal = new value.constructor()
hash.set(value, cloneVal)
for (let k in value) {
cloneVal[k] = deepClone(value[k], hash)
}
return cloneVal
}
let b = {b:{c:1}}
b.x = b
let newB = deepClone(b)
console.log(newB)