深拷贝和浅拷贝

浅拷贝: 对象只被克隆了一个引用或者只会被克隆最外部的一层,更深层的对象还是通过引用指向同一块堆内存。

1. Object.assign()

 1 let p1= { 
 2     name: 'Tom' ,
 3     children: [{
 4         name: 'A'
 5     }]
 6 };
 7 let p2 = Object.assign({}, p1);
 8 p1.name = 'Tim';
 9 console.log(p2.name); // Tom
10 p1.children[0].name = 'B';
11 console.log(p2.children[0].name); // B

2. 展开运算符 ...

1 let p1 = {
2     name: 'Tom',
3     children: [{
4         name: 'A'
5     }]
6 };
7 let p2 = { ...p1 };
8 p1.name = 'Tim';
9 console.log(p1.name); // Tom

3. 自己编写一个函数

1 function shallowClone(obj) {
2     const result = {};
3     for (let key in obj) {
4         result[key] = obj[key];
5     }
6     return obj;
7 }

深拷贝:不光解决了第一层的问题,还递归拷贝了目标对象的所有属性。

1. JSON.parse(JSON.strigify())

 1 function Person(name) { 
 2     this.name = name;
 3 }
 4 const p = new Person('Tom');
 5 
 6 const oldObj = {
 7     a: function() { console.log('hi');},
 8     b: new Array(1),
 9     c: new RegExp('ab+c', 'i'),
10     d: p,
11     e: Symbol('a'),
12     f: undefined
13 };
14 
15 const newObj = JSON.parse(JSON.stringify(oldObj));
16 
17 console.log(oldObj);
18 //{ 
19     a: ƒ (),
20     b: [empty],
21     c: /ab+c/i,
22     d: Person {name: "Tom"},
23     e: Symbol(male),
24     f: undefined
25 }
26 console.log(newObj);
27 //{
28     b: [null],
29     c: {},
30     d: {name: "Tom"}
31 }

 

缺点:

  • 无法实现对函数、undefined、regexp、symbol等类型克隆
  • 会抛弃对象的constructor,所有的构造函数都会指向object
  • 对象有循环引用会报错

2. 简单的深拷贝(不考虑函数、Date、RegExp等类型)

1 const deepClone = (obj) => {
2     if (obj === null || typeof obj !== 'object') return obj;
3     let cloneObj = Array.isArray(obj) ? [] : {};
4     for(let key in obj) {
5         if (!obj.hasOwnProperty(key)) return;
6         cloneObj[key] = obj[key] !== null && typeof obj === 'object' ? deepClone(obj[key]) : obj[key];
7     }
8     return cloneObj;
9 }

3. 考虑部分类型的简易版深拷贝,推荐使用 lodash 的深拷贝函数。

 1 const isType = (obj, type) => Object.prototype.toString.call(obj).slice(8, -1) === type;
 2 
 3 // 可用es6的flags替换(ie不支持)
 4 const getRegExp = re => {
 5     var result = '';
 6     if (re.global) result += 'g';
 7     if (re.ignoreCase) result += 'I';
 8     if (re.multiline) result += 'm';
 9     return result;
10 }
11 
12 const deepClone = (obj) => {
13     const oldList = [];
14     const newList = [];
15     const _clone = (parent) => {
16         if (parent === null || typeof parent !== 'object') return parent;
17         let result;
18         if (isType(parent, 'Array')) {
19             result = [];
20         } else if (isType(parent, 'RegExp')) {
21             result = new RegExp(parent.source, getRegExp(parent)); // 等同于 parent.flags
22             if (parent.lastIndex) resut.lastIndex = parent.lastIndex;
23         } else if (isType(parent, 'Date')) {
24             result = new Date(parent.getTime());
25         } else {
26             result = Object.create(Object.getPrototypeOf(parent));
27         }
28         // 处理循环引用
29         const index = oldList.indexOf(parent);
30         if (index !== -1) {
31             return newList[index];
32         }
33         oldList.push(parent);
34         newList.push(result);
35 
36         for (let attr in parent) {
37             // 递归调用
38             result[attr] = _clone(parent[attr]);
39         }
40         return result;
41     };
42     return _clone(obj);
43 }

4. MessageChannel

 1 // 若所需拷贝的对象含有内置类型并且不包含函数,可以使用MessageChannel
 2 
 3 const structuralClone = obj => {
 4     return new Promise(resolve => {
 5         const {port1, port2} = new MessageChannel();
 6         port2.onmessage = ev => resolve(ev.data);
 7         port1.postMessage(obj);
 8     });
 9 };
10 
11 const test = async () => {
12     const clone = await structuralClone(obj);
13     console.log(clone);
14 };
15 test();

 

posted @ 2019-02-13 11:59  格洛米miss  阅读(180)  评论(0编辑  收藏  举报