diff --git a/src/index.js b/src/index.js index c84a844..dd9376d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ import { type } from '@jsmini/type'; + // Object.create(null) 的对象,没有hasOwnProperty方法 function hasOwnProp(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); @@ -8,7 +9,7 @@ function hasOwnProp(obj, key) { // 仅对对象和数组进行深拷贝,其他类型,直接返回 function isClone(x) { const t = type(x); - return t === 'object' || t === 'array'; + return t === 'object' || t === 'array' || t === 'set' || t === 'map'; } // 递归 @@ -130,6 +131,11 @@ export function cloneLoop(x) { } const UNIQUE_KEY = 'com.yanhaijing.jsmini.clone' + (new Date).getTime(); +const UNIQUE_SET_KEY = 'com.yanhaijing.jsmini.clone.set' + (new Date).getTime(); +const UNIQUE_MAPKEY_KEY = 'com.yanhaijing.jsmini.clone.mapkey' + (new Date).getTime(); +const UNIQUE_MAPVALUE_KEY = 'com.yanhaijing.jsmini.clone.mapvalue' + (new Date).getTime(); + + // weakmap:处理对象关联引用 function SimpleWeakmap (){ @@ -164,24 +170,68 @@ function getWeakMap(){ return result; } -export function cloneForce(x) { - const uniqueData = getWeakMap(); +function setValueToParent(parent,key,value){ + + if(key === UNIQUE_SET_KEY){ + parent.add(value); + }else if(key == UNIQUE_MAPVALUE_KEY){ //控制数据循环的顺序,先添加value,再添加key + parent.set(UNIQUE_MAPKEY_KEY,value); + }else if(key == UNIQUE_MAPKEY_KEY){ + parent.set(value,parent.get(UNIQUE_MAPKEY_KEY)); + parent.delete(UNIQUE_MAPKEY_KEY); + }else{ + parent[key] = value; + } + return value; +} - const t = type(x); - let root = x; +//检测Set功能 +const checkSet = (function(){ + try { + let set = new Set(); + set.add(UNIQUE_KEY); - if (t === 'array') { - root = []; - } else if (t === 'object') { - root = {}; + if(set.has(UNIQUE_KEY)){ + + set.delete(UNIQUE_KEY); + return true; + } + } catch (e) { + console.log(e.message); } + return false; +})(); +//检测Map的功能 +const checkMap = (function(){ + try { + let map = new Map(); + map.set(UNIQUE_KEY,'Map'); + + if(map.has(UNIQUE_KEY) && map.get(UNIQUE_KEY) == 'Map'){ + + map.delete(UNIQUE_KEY); + return true; + } + map.delete(UNIQUE_KEY); + } catch (e) { + console.log(e.message); + } + return false; +})(); + +export function cloneForce(x) { + const uniqueData = getWeakMap(); + + let root = { + next:x + }; // 循环数组 const loopList = [ { parent: root, - key: undefined, + key: 'next', data: x, } ]; @@ -194,59 +244,80 @@ export function cloneForce(x) { const source = node.data; const tt = type(source); - // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素 - let target = parent; - if (typeof key !== 'undefined') { - target = parent[key] = tt === 'array' ? [] : {}; - } - // 复杂数据需要缓存操作 if (isClone(source)) { // 命中缓存,直接返回缓存数据 let uniqueTarget = uniqueData.get(source); if (uniqueTarget) { - parent[key] = uniqueTarget; + setValueToParent(parent,key,uniqueTarget); continue; // 中断本次循环 } - - // 未命中缓存,保存到缓存 - uniqueData.set(source, target); } + let newValue; if (tt === 'array') { + newValue = setValueToParent(parent,key,[]); for (let i = 0; i < source.length; i++) { - if (isClone(source[i])) { - // 下一次循环 - loopList.push({ - parent: target, - key: i, - data: source[i], - }); - } else { - target[i] = source[i]; - } + // 下一次循环 + loopList.push({ + parent: newValue, + key: i, + data: source[i], + }); } } else if (tt === 'object'){ + newValue = setValueToParent(parent,key,{}); for(let k in source) { + if(k === UNIQUE_KEY) continue; if (hasOwnProp(source, k)) { - if(k === UNIQUE_KEY) continue; - if (isClone(source[k])) { - // 下一次循环 - loopList.push({ - parent: target, - key: k, - data: source[k], - }); - } else { - target[k] = source[k]; - } + // 下一次循环 + loopList.push({ + parent: newValue, + key: k, + data: source[k], + }); } } + } else if (tt === 'set' && checkSet){ + newValue = setValueToParent(parent,key,new Set()); + for (let s of source){ + // 下一次循环 + loopList.push({ + parent: newValue, + key: UNIQUE_SET_KEY, + data: s, + }); + } + } else if (tt === 'map' && checkMap){ + newValue = setValueToParent(parent,key,new Map()); + for (let m of source){ + // 下一次循环 + //先添加key, 再添加value 和 setValueToParent里保持一致 + loopList.push({ + parent: newValue, + key: UNIQUE_MAPKEY_KEY, + data: m[0], + }); + loopList.push({ + parent: newValue, + key: UNIQUE_MAPVALUE_KEY, + data: m[1], + }); + } + } else{ + setValueToParent(parent,key,source); + continue; } + + // 未命中缓存,保存到缓存 + uniqueData.set(source, newValue); + } uniqueData.clear && uniqueData.clear(); - return root; + return root.next; } + + diff --git a/test/test.js b/test/test.js index 9d8de9f..af5a1c9 100644 --- a/test/test.js +++ b/test/test.js @@ -1,4 +1,5 @@ var expect = require('expect.js'); +var type = require('@jsmini/type').type; var clone = require('../dist/index.js').clone; var cloneJSON = require('../dist/index.js').cloneJSON; @@ -28,6 +29,8 @@ describe('单元测试', function() { } ]; + + // 正常cases var normalList = [ { @@ -53,6 +56,67 @@ describe('单元测试', function() { } ]; + //添加set数据 + try { + var normalList1 = [ + { + a: [], + }, + { + a: [1, 2, 3], + }, + { + a: [1, [2, [3]]], + }, + { + a: {}, + }, + { + a: {a: 1, b: 2, c: 3}, + }, + { + a: {a1: 1, a2: {b1: 1, b2: {c1: 1, c2: 2}}}, + }, + { + a: {a1: 1, a2: [1, {b1: 1, b2: [{c1: 1, c2: 2}]}]} + } + ]; + var set = new Set() + var set1 = new Set() + set.add(1) + set.add(set1) + set.add({ + a: 1, + a1:[set1], + a2:{ + a:[set1] + } + }) + set.add([ + 'abc' + ]) + set.add(null) + set.add(undefined) + normalList1.push({ + a:set, + }) + + var map = new Map() + var obj = {} + map.set(1,1) + map.set(2,'a') + map.set('a',null) + map.set('b',1) + map.set(4,undefined) + map.set({},1) + map.set(obj,obj) + normalList1.push({ + a:map, + }) + } catch (error) { + + } + // 父子循环引用 var a = [1, 2, 3]; a.push(a); @@ -71,6 +135,58 @@ describe('单元测试', function() { } ]; + //添加set数据 + try { + var singleRefList1 = [ + { + a: a, + }, + { + a: b, + }, + { + a: b, + } + ]; + var set = new Set() + + set.add(set) + + var set1 = new Set() + + set1.add(set) + + singleRefList1.push({ + a:set, + }) + singleRefList1.push({ + a:set1, + }) + + var map = new Map() + map.set('a',map) + + map.set(map,'a') + + var map1 = new Map() + + map1.set('a',{ + 'a4':map1 + }) + map1.set({ + 'a4':map1 + },'a') + + singleRefList1.push({ + a:map, + }) + singleRefList1.push({ + a:map1, + }) + } catch (error) { + + } + // 多层级循环引用 var a = [1, [2]]; a[1].push(a); @@ -88,6 +204,51 @@ describe('单元测试', function() { a: b, } ]; + //添加set数据 + try { + var complexRefList1 = [ + { + a: a, + }, + { + a: b, + }, + { + a: b, + } + ]; + var set = new Set() + var set1 = new Set() + var set2 = new Set() + set.add(set) + + complexRefList1.push({ + a:set + }) + set1.add(set2) + set2.add({ + b:set1 + }) + set2.add(set1) + complexRefList1.push({ + a:set1 + }) + + + var map = new Map() + var map1 = new Map() + var map2 = new Map() + map1.set('b',map2) + map1.set('a',map) + map1.set(map,'a') + map.set('a',map1) + map.set(map1,'b') + complexRefList1.push({ + a:map + }) + } catch (error) { + + } describe('clone', function() { it('常规', function() { for (var i = 0; i < simpleList.length; i++) { @@ -165,30 +326,86 @@ describe('单元测试', function() { expect(cloneForce(simpleList[i].a)).to.be(simpleList[i].a); } - for (var i = 0; i < normalList.length; i++) { - var temp = cloneForce(normalList[i].a); - + for (var i = 0; i < normalList1.length; i++) { + var temp = cloneForce(normalList1[i].a); // 确保不全等 - expect(temp).not.to.be(normalList[i].a); + expect(temp).not.to.be(normalList1[i].a); // 确保内容一样 - expect(temp).to.eql(normalList[i].a); + expect(temp).to.eql(normalList1[i].a); } }); it('简单循环引用', function() { - var temp = cloneForce(singleRefList[0].a); + var temp = cloneForce(singleRefList1[0].a); expect(temp).to.be(temp[3]); - var temp = cloneForce(singleRefList[1].a); + var temp = cloneForce(singleRefList1[1].a); expect(temp).to.be(temp['a4']); + + //set数据检测 + if(singleRefList1[3]){ + var temp = cloneForce(singleRefList1[3].a); + expect(temp.has(temp)).to.be(true); + var temp1 = cloneForce(singleRefList1[4].a); + expect(temp1.has(temp1)).not.to.be(true); + } + + //map数据检测 + + if(singleRefList1[5]){ + var temp = cloneForce(singleRefList1[5].a); + expect(temp.has(temp)).to.be(true); + expect(temp.has(temp.get('a'))).to.be(true); + + var temp = cloneForce(singleRefList1[6].a); + // expect(temp.has(temp)).to.be(true); + expect(temp.get('a')['a4']).to.be(temp); + } + + }); it('复杂循环引用', function() { - var temp = cloneForce(complexRefList[0].a); + var temp = cloneForce(complexRefList1[0].a); expect(temp).to.be(temp[1][1]); - var temp = cloneForce(complexRefList[1].a); + + var temp = cloneForce(complexRefList1[1].a); expect(temp).to.be(temp.a2.b2); + + //set数据的检测 + if (complexRefList1[3]){ + var temp = cloneForce(complexRefList1[3].a); + expect(temp.has(temp)).to.be(true); + var temp = cloneForce(complexRefList1[4].a); + var values = temp.values() + var temp1 = values.next().value + values = temp1.values() + var value1 = values.next().value + var value2 = values.next().value + + if (type(value1) == 'object'){ + expect(temp).to.be(value1.b); + expect(temp).to.be(value2); + + }else{ + expect(temp).to.be(value2.b); + expect(temp).to.be(value1); + } + } + + //map数据检测 + + if(complexRefList1[5]){ + + var temp = cloneForce(complexRefList1[5].a); + + expect(temp.get('a').get('a')).to.be(temp); + expect(temp.get('a').get('b')).not.to.be(temp); + expect(temp.get(temp.get('a'))).to.be('b') + expect(temp.get('a').get(temp.get('a').get('a'))).to.be('a') + } + }); }); });