前端工具代码详解(有防抖、节流等等常用工具解读)
第四章 深拷贝浅拷贝(一行一行分析代码带你深入了解深浅拷贝)
文章目录
序言
所有的详细解析,都在代码的注释中。我建议大家做好看完后,自己思考一下,然后根据自己的感觉来写一遍,这样子可以加强自己的记忆,并且可以对比一下。感谢观看,期待点赞收藏关注,感谢读者。
一、浅拷贝一 (基础版)
代码如下
let obj1 = {
a: 1,
b: { a: 2, c: 5 },
}
let obj2 = {
b: { a: 5, b: 3 },
c: 4,
}
let obj3 = {
d: 4,
}
// 初始版本
function extend1() {
let nowData = arguments[0]
for (i = 1; i < arguments.length; i++) {
let options = arguments[i]
if (options) {
for (const key in options) {
if (Object.hasOwnProperty.call(options, key)) {
const element = options[key]
nowData[key] = element
}
}
}
}
return nowData
}
二、优化浅拷贝(提高了性能)
代码如下:
// 优化版本,将 element options name 都在外部创建,这样子就不用每一次循环都创建,只需要赋值
// 这样子效率更高
// 上述方法 需要 0.090ms ~ 0.12ms 左右 extend1
// 但是extend 只要0.067 ~ 0.072ms左右
function extend() {
let name, options, copy
let length = arguments.length
// 因为 0 是目标对象,后续的都要放到 对象 0 里面
// 所以 i = 1
let i = 1
// 存放目标对象,把后续都放到目的对象 target中 之后返回
let target = arguments[0]
for (; i < length; i++) {
options = arguments[i]
if (options != null) {
// 因为都是对象,所以遍历对象
for (name in options) {
copy = options[name]
if (copy !== undefined) {
target[name] = copy
}
}
}
}
return target
}
大家可以使用 console.time() 和 console.timeEnd()方法来看看效率,就可以看到使用的时间了
console.time()
let newObj = extend1(obj1, obj2, obj3)
console.timeEnd()
console.time()
let newObj = extend(obj1, obj2, obj3)
console.timeEnd()
三.升级版本(职场深拷贝,传递第一个参数为 true 就开启深拷贝,默认为浅拷贝,可以不传)
代码如下
var class2type = {}
var toString = class2type.toString
var hasOwn = class2type.hasOwnProperty
function isPlainObject(obj) {
var proto, Ctor
if (!obj || toString.call(obj) !== '[object Object]') {
return false
}
proto = Object.getPrototypeOf(obj)
if (!proto) {
return true
}
Ctor = hasOwn.call(proto, 'constructor') && proto.constructor
return (
typeof Ctor === 'function' &&
hasOwn.toString.call(Ctor) === hasOwn.toString.call(Object)
)
}
function isFunction(obj) {
return typeof obj === 'function'
}
// 升级版,根据第一个参数,确定要不要深拷贝
function extend2() {
// 默认不要深拷贝(深拷贝性能消耗大)因为要递归
let deep = false
let name, options, src, copy, clone, copyIsArray
let length = arguments.length
// 如果第一个值没有传递 deep 的值,就从 1 开始循环
let i = 1
// 获取第一个值
let target = arguments[0]
// 如果第一个值传递了deep
if (typeof target === 'boolean') {
deep = target
target = arguments[1] || {}
i++
}
// 如果target不是对象,无法进行复制,函数是可以复制的,所以要判断
// 不是对象 也不是函数的时候,给target赋值为{}
if (typeof target !== 'object' && !isFunction(target)) {
target = {}
}
// 循环遍历对象
for (; i < length; i++) {
// 获取当前对象
options = arguments[i]
if (options != null) {
for (name in options) {
// 目标属性值,假设如果target[name] 是对象,并且要深拷贝就需要使用
src = target[name]
copy = options[name]
// 防止循环引用
if (target === copy) {
continue
}
// 判断 src 的数据类型 和 copy的类型是否一致
// 如果不一致,转换为相同的类型
if (
deep &&
copy &&
(isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))
) {
if (copyIsArray) {
copyIsArray = false
clone = src && Array.isArray(src) ? src : []
} else {
clone = src && isPlainObject(src) ? src : {}
}
target[name] = extend(deep, clone, copy)
} else if (copy !== undefined) {
target[name] = copy
}
}
}
}
return target
}
总结
以上就是关于深拷贝和浅拷贝的实现方法了,大家可以仔细观看,最好看完后,自己敲一遍,不要看着代码敲,根据自己的想法写。最后感谢观看,希望大家一起变强!