跳至主要內容

前端深拷贝实现

执念Web基础deepClone大约 2 分钟约 716 字...

前端深拷贝实现

1. JSON.parse(JSON.stringify(obj))

JSON.parse(JSON.stringify(obj))实现深拷贝的过程是利用JSON.stringify()将对象序列化(转换成一个JSON字符串),再使用JSON.parse()进行反序列化(还原)。

弊端

如果 obj 里有时间对象,则JSON.parse(JSON.stringify(obj))的结果将只是字符串,而不是时间对象:

const date = new Date();
const newDate = JSON.parse(JSON.stringify(date));
console.log(typeof newDate); // string

如果 obj 里有 RegExp、Error 对象,则序列化的结果将只会得到空对象:

const reg = new RegExp();
const err = new Error();
const newReg = JSON.parse(JSON.stringify(reg));
const newErr = JSON.parse(JSON.stringify(err));
console.log(newReg); // {}
console.log(newErr); // {}

如果 obj 里有函数、undefined,则序列化的结果会将函数、undefined 丢失:

const object = {
    fn: () => {},
    x: undefined,
}
const newObject = JSON.parse(JSON.stringify(object));
console.log(newObject); // {}

如果 obj 里有 NaN、Infinity、-Infinity,则序列化的结果会得到null

const object = {
    x: NaN,
    y: Infinity,
    z: -Infinity,
}
const newObject = JSON.parse(JSON.stringify(object));
console.log(newObject); // {x: null, y: null, z: null}

JSON.stringify()只能序列化对象的可枚举的自有属性,如果 obj 中有对象是构造函数生成的,则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢失对象的 constructor:

function Person(name, age) {
    this.name = name;
    this.age = age;
}
const dlrb = new Person('迪丽热巴', 18);

const object = {
    x: dlrb
}

const newObject = JSON.parse(JSON.stringify(object));
console.log(newObject.x.__proto__.constructor === Person); // fasle

如果 obj 中存在循环引用,会陷入死循环:

const object = {
    x: 1,
}
object.object = object;
const newObject = JSON.parse(JSON.stringify(object)); // error

排除以上情况,可以使用JSON.parse(JSON.stringify(obj))来实现深拷贝。

2. 封装deepClone函数

function deepClone(target){ 
    if(target !== null && typeof target === 'object'){ 
        let result = Object.prototype.toString.call(target) === "[object Array]" ? [] : {};
        for (let k in target){ 
            if (target.hasOwnProperty(k)) { 
                result[k] = deepClone(target[k]) 
            } 
        } 
        return result; 
    }else{ 
        return target; 
    } 
}

以上代码中,deepClone函数的参数 target 是要深拷贝的数据。

执行 target !== null && typeof target === 'object' 判断 target 是不是引用类型。

若不是,直接返回 target

若是,判断引用类型的数据是对象类型还是数组类型,然后创建一个变量 result 作为深拷贝的结果,遍历 target,执行 deepClone(target[k])target 每个属性的值深拷贝后赋值到深拷贝的结果对应的属性 result[k] 上,遍历完毕后返回 result

在执行 deepClone(target[k]) 中,又会对 target[k] 进行类型判断,重复上述流程,形成了一个递归调用 deepClone 函数的过程。就可以层层遍历要拷贝的数据,不管要拷贝的数据有多少子属性,只要子属性的值的类型是引用类型,就会调用 deepClone 函数将其深拷贝后赋值到深拷贝的结果对应的属性上。

另外使用 for...in 循环遍历对象的属性时,其原型链上的所有属性都将被访问,如果只要只遍历对象自身的属性,而不遍历继承于原型链上的属性,要使用 hasOwnProperty 方法过滤一下。

上次编辑于:
贡献者: 夏宇,ixiayu1
你认为这篇文章怎么样?
  • 0
  • 0
  • 0
  • 0
  • 0
  • 0
评论
  • 按正序
  • 按倒序
  • 按热度