JavaScript 中的拷贝: 其实就是一个对象复制给另外一整个对象, 让对象相互不影响. 对象的拷贝又分为浅拷贝和深拷贝.
目前基本数据类型有: Boolean,Null,Undefined,Number,String,Symbol, 引用数据类型有: Object,Array,Function,RegExp,Date 等. 深拷贝与浅拷贝的概念只存在于引用数据类型.
一, JavaScript 自带的深拷贝方法
1,Array
slice(),concat,Array.from(),... 操作符: 只能实现一维数组的深拷贝
- var arr1 = [1, 2, [3, 4]],
- arr2 = arr1.slice();
- console.log(arr1); //[1, 2, [3, 4]]
- console.log(arr2); //[1, 2, [3, 4]]
- arr2[0] = 2 arr2[2][1] = 5;
- console.log(arr1); //[1, 2, [3, 5]]
- console.log(arr2); //[2, 2, [3, 5]]
- 2,Object
Object.assign(): 只能实现一维对象的深拷贝
- var obj1 = {
- x: 1,
- y: 2
- },
- obj2 = Object.assign({},
- obj1);
- console.log(obj1) //{x: 1, y: 2}
- console.log(obj2) //{x: 1, y: 2}
- obj2.x = 2; // 修改 obj2.x
- console.log(obj1) //{x: 1, y: 2}
- console.log(obj2) //{x: 2, y: 2}
- var obj1 = {
- x: 1,
- y: {
- m: 1
- }
- };
- var obj2 = Object.assign({},
- obj1);
- console.log(obj1) //{x: 1, y: {m: 1}}
- console.log(obj2) //{x: 1, y: {m: 1}}
- obj2.y.m = 2; // 修改 obj2.y.m
- console.log(obj1) //{x: 1, y: {m: 2}}
- console.log(obj2) //{x: 2, y: {m: 2}}
JSON.parse(JSON.stringify(obj)): 可实现多维对象的深拷贝, 但会忽略 undefined, 任意的函数, symbol 值
- var obj1 = {
- x: 1,
- y: {
- m: 1
- },
- a: undefined,
- b: function(a, b) {
- return a + b
- },
- c: Symbol("foo")
- };
- var obj2 = JSON.parse(JSON.stringify(obj1));
- console.log(obj1) //{x: 1, y: {m: 1}, a: undefined, b: ƒ, c: Symbol(foo)}
- console.log(obj2) //{x: 1, y: {m: 1}}
- obj2.y.m = 2; // 修改 obj2.y.m
- console.log(obj1) //{x: 1, y: {m: 1}, a: undefined, b: ƒ, c: Symbol(foo)}
- console.log(obj2) //{x: 2, y: {m: 2}}
注: 进行 JSON.stringify()序列化的过程中, undefined, 任意的函数以及 symbol 值, 在序列化过程中会被忽略 (出现在非数组对象的属性值中时) 或者被转换成 null(出现在数组中时).
由上面可知, JS 提供的自有方法并不能彻底解决 Array,Object 的深拷贝问题, 因此我们应该自己实现.
二, 深拷贝函数(支持基本数据类型, 原型链, RegExp,Date 类型)
- function deepClone(obj, parent = null) {
- let result; // 最后的返回结果
- let _parent = parent; // 防止循环引用
- while (_parent) {
- if (_parent.originalParent === obj) {
- return _parent.currentParent;
- }
- _parent = _parent.parent;
- }
- if (obj && typeof obj === "object") { // 返回引用数据类型(null 已被判断条件排除))
- if (obj instanceof RegExp) { // RegExp 类型
- result = new RegExp(obj.source, obj.flags)
- } else if (obj instanceof Date) { // Date 类型
- result = new Date(obj.getTime());
- } else {
- if (obj instanceof Array) { // Array 类型
- result = []
- } else { // Object 类型, 继承原型链
- let proto = Object.getPrototypeOf(obj);
- result = Object.create(proto);
- }
- for (let key in obj) { // Array 类型 与 Object 类型 的深拷贝
- if (obj.hasOwnProperty(key)) {
- if (obj[key] && typeof obj[key] === "object") {
- result[key] = deepClone(obj[key], {
- originalParent: obj,
- currentParent: result,
- parent: parent
- });
- } else {
- result[key] = obj[key];
- }
- }
- }
- }
- } else { // 返回基本数据类型与 Function 类型, 因为 Function 不需要深拷贝
- return obj
- }
- return result;
- }
- // 调试用
- function construct() {
- this.a = 1,
- this.b = {
- x: 2,
- y: 3,
- z: [4, 5, [6]]
- },
- this.c = [7, 8, [9, 10]],
- this.d = new Date(),
- this.e = /abc/ig,
- this.f = function(a, b) {
- return a + b
- },
- this.g = null,
- this.h = undefined,
- this.i = "hello",
- this.j = Symbol("foo")
- }
- construct.prototype.str = "I'm prototype"
- var obj1 = new construct()
- obj1.k = obj1
- obj2 = deepClone(obj1)
- obj2.b.x = 999
- obj2.c[0] = 666
- console.log(obj1)
- console.log(obj2)
- console.log(obj1.str)
- console.log(obj2.str)
注: Function 类型的深拷贝:
bind(): 使用 fn.bind()可将函数进行深拷贝, 但因为 this 指针指向问题而不能使用;
eval(fn.toString()): 只支持箭头函数, 普通函数 function fn(){}则不适用;
new Function(arg1,arg2,...,function_body): 需将参数与函数体提取出来;
来源: http://www.css88.com/qa/javascript/11646.html